TCP定时器 之 连接建立定时器
当服务器收到新的syn请求,会回复syn+ack给请求端,若某时间内未收到请求端回复的ack,新建连接定时器超时执行回调,重传syn+ack,当超时超过固定次数时,该连接中止;本文主要分析其初始化流程,具体的建立连接和超时重传流程在后续的文章中进行详细讨论;
request_sock结构中的rsk_timer成员为新建连接计时器;
1 /* struct request_sock - mini sock to represent a connection request 2 */ 3 struct request_sock { 4 struct sock_common __req_common; 5 /* 省略了一些字段 */ 6 struct timer_list rsk_timer; 7 const struct request_sock_ops *rsk_ops; 8 struct sock *sk; 9 /* 省略了一些字段 */ 10 };
函数调用关系如下,其中tcp_rcv_state_process中判断标记,如果发生是设置了syn请求标记,则进入新建连接流程,在tcp_conn_request中将会新建连接请求控制块,用于跟踪完成三次握手;
1 /** 2 *tcp_v4_rcv 3 * |-->tcp_v4_do_rcv 4 * |-->tcp_rcv_state_process /* 这里如果是syn请求,则调用下面的conn_request函数 */ 5 * |-->tcp_v4_conn_request 6 * |-->tcp_conn_request /* 这里新建请求控制块 */ 7 * |-->inet_csk_reqsk_queue_hash_add 8 * |-->reqsk_queue_hash_req 9 */
启动定时器:
reqsk_queue_hash_req函数进行连接请求定时器的设定,将req->rsk_timer的超时回调设置为reqsk_timer_handler,reqsk_timer_handler调用inet_rtx_syn_ack进行syn+ack的重传;
其中超时时间初始化为timeout=TCP_TIMEOUT_INIT,为1HZ;timeout会随着重传的次数不断变化timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);
1 static void reqsk_queue_hash_req(struct request_sock *req, 2 unsigned long timeout) 3 { 4 req->num_retrans = 0; 5 req->num_timeout = 0; 6 req->sk = NULL; 7 8 /* 添加定时器 */ 9 setup_pinned_timer(&req->rsk_timer, reqsk_timer_handler, 10 (unsigned long)req); 11 mod_timer(&req->rsk_timer, jiffies + timeout); 12 13 inet_ehash_insert(req_to_sk(req), NULL); 14 /* before letting lookups find us, make sure all req fields 15 * are committed to memory and refcnt initialized. 16 */ 17 smp_wmb(); 18 atomic_set(&req->rsk_refcnt, 2 + 1); 19 }
定时器回调函数:
判断是否超时次数超过阈值,是否需要重传syn+ack,如果超时或者未收到ack情况下重传失败,则取消连接;函数中还包含了对refer_accept选项的处理;
1 /* 新建连接定时器超时处理 */ 2 static void reqsk_timer_handler(unsigned long data) 3 { 4 struct request_sock *req = (struct request_sock *)data; 5 struct sock *sk_listener = req->rsk_listener; 6 struct net *net = sock_net(sk_listener); 7 struct inet_connection_sock *icsk = inet_csk(sk_listener); 8 struct request_sock_queue *queue = &icsk->icsk_accept_queue; 9 int qlen, expire = 0, resend = 0; 10 int max_retries, thresh; 11 u8 defer_accept; 12 13 /* 不是LISTEN状态 */ 14 if (sk_state_load(sk_listener) != TCP_LISTEN) 15 goto drop; 16 17 /* 阈值设置为允许的最大重传数 */ 18 max_retries = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_synack_retries; 19 thresh = max_retries; 20 /* Normally all the openreqs are young and become mature 21 * (i.e. converted to established socket) for first timeout. 22 * If synack was not acknowledged for 1 second, it means 23 * one of the following things: synack was lost, ack was lost, 24 * rtt is high or nobody planned to ack (i.e. synflood). 25 * When server is a bit loaded, queue is populated with old 26 * open requests, reducing effective size of queue. 27 * When server is well loaded, queue size reduces to zero 28 * after several minutes of work. It is not synflood, 29 * it is normal operation. The solution is pruning 30 * too old entries overriding normal timeout, when 31 * situation becomes dangerous. 32 * 33 * Essentially, we reserve half of room for young 34 * embrions; and abort old ones without pity, if old 35 * ones are about to clog our table. 36 */ 37 38 /* 获取accept队列长度 */ 39 qlen = reqsk_queue_len(queue); 40 41 /* 请求accept队列数> 最大未完成连接数的一半 */ 42 if ((qlen << 1) > max(8U, sk_listener->sk_max_ack_backlog)) { 43 44 /* 没有重传ack的请求控制块数 */ 45 int young = reqsk_queue_len_young(queue) << 1; 46 47 /* 可以看出年轻的数量越多,则允许重传次数越多 */ 48 while (thresh > 2) { 49 50 // 没重传的数量> 队列长度的(1/2, 1/4,1/8...) 51 if (qlen < young) 52 break; 53 54 /* 阈值减1 */ 55 thresh--; 56 57 /* 扩大young */ 58 young <<= 1; 59 } 60 } 61 /* 设置了TCP_DEFER_ACCEPT,则使用之 */ 62 defer_accept = READ_ONCE(queue->rskq_defer_accept); 63 if (defer_accept) 64 max_retries = defer_accept; 65 66 /* 超时和是否重传判断 */ 67 syn_ack_recalc(req, thresh, max_retries, defer_accept, 68 &expire, &resend); 69 /* 统计超时次数 */ 70 req->rsk_ops->syn_ack_timeout(req); 71 if (!expire && /* 未超时 */ 72 (!resend || /* 不需要重传 */ 73 !inet_rtx_syn_ack(sk_listener, req) || /* 重传成功 */ 74 inet_rsk(req)->acked)) { /* 收到ack了,但是未建立连接(defer_accept,其他情况?) */ 75 unsigned long timeo; 76 77 /* 该请求尚未重传过,则将未重传块-1 */ 78 /* 超时次数+ 1 */ 79 if (req->num_timeout++ == 0) 80 atomic_dec(&queue->young); 81 82 /* 重新设定超时时间为上次时间* 2,与pto_max取最小值 */ 83 timeo = min(TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX); 84 mod_timer(&req->rsk_timer, jiffies + timeo); 85 return; 86 } 87 88 /* 取消连接,从链表移除请求控制块 */ 89 drop: 90 inet_csk_reqsk_queue_drop_and_put(sk_listener, req); 91 }
判断是否已经超时,或者是否需要重传syn+ack;
1 /* Decide when to expire the request and when to resend SYN-ACK */ 2 /* 判断是否超时,是否重传syn+ack */ 3 static inline void syn_ack_recalc(struct request_sock *req, const int thresh, 4 const int max_retries, 5 const u8 rskq_defer_accept, 6 int *expire, int *resend) 7 { 8 /* 无defer_accept选项 */ 9 if (!rskq_defer_accept) { 10 /* 超时次数> 重传阈值则超时 */ 11 *expire = req->num_timeout >= thresh; 12 13 /* 需要重传 */ 14 *resend = 1; 15 return; 16 } 17 18 /* 以下设置了defer_accept */ 19 20 /* 超时次数> 重传阈值&& (未收到ack || 超时次数>= 最大重传数),则超时 */ 21 /* 已经收到ack的情况下,超时次数达到defer_accept限制 */ 22 *expire = req->num_timeout >= thresh && 23 (!inet_rsk(req)->acked || req->num_timeout >= max_retries); 24 /* 25 * Do not resend while waiting for data after ACK, 26 * start to resend on end of deferring period to give 27 * last chance for data or ACK to create established socket. 28 */ 29 30 /* 未收到ack || 收到ack,但是超时次数达到了defer_accept限制-1,需要重传*/ 31 /* 给了设置defer_accept选项情况下一次机会 */ 32 *resend = !inet_rsk(req)->acked || 33 req->num_timeout >= rskq_defer_accept - 1; 34 }