假设客户端执行主动打开,已经经过第一次握手,即发送SYN包到服务器,状态变为SYN_SENT,服务器收到该包后,回复SYN+ACK包,客户端收到该包,进行主动打开端的第二次握手部分;流程中涉及到的函数和细节非常多,本篇只对主流程予以分析;

在ESTABLISHED和TIME_WAIT以外的状态时接收到包,会调用tcp_rcv_state_process函数来处理,处理部根据不同状态做对应处理,如果处于SYN_SENT状态,则会调用tcp_rcv_synsent_state_process函数进入和该状态的核心处理流程;

 1 /*
 2  *    This function implements the receiving procedure of RFC 793 for
 3  *    all states except ESTABLISHED and TIME_WAIT.
 4  *    It's called from both tcp_v4_rcv and tcp_v6_rcv and should be
 5  *    address independent.
 6  */
 7 
 8 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
 9 {
10         switch (sk->sk_state) {
11     case TCP_CLOSE:
12         goto discard;
13 
14     case TCP_LISTEN:
15                /* 省略部分代码 */    
16 
17     case TCP_SYN_SENT:
18         tp->rx_opt.saw_tstamp = 0;
19         skb_mstamp_get(&tp->tcp_mstamp);
20 
21         /* syn_sent状态处理 */
22         queued = tcp_rcv_synsent_state_process(sk, skb, th);
23 
24         /* 下面返回值> 0,调用者需要发送rst */
25         if (queued >= 0)
26             return queued;
27 
28         /* Do step6 onward by hand. */
29         /* 处理紧急数据 */
30         tcp_urg(sk, skb, th);
31         __kfree_skb(skb);
32 
33         /* 检查是否有数据要发送 */
34         tcp_data_snd_check(sk);
35         return 0;
36     }
37 
38 }

 

tcp_rcv_synsent_state_process函数为SYN_SENT状态输入数据包的核心处理函数,这里我们只关系服务器发送SYN+ACK的处理流程,其他部分暂且省略;函数主要完成以下几项工作:解析tcp选项;对序号,时间错,标志位等合法性做检查;调用函数tcp_ack对ack进行慢速路径处理;进行窗口,时间戳选项,MSS最大报文段,PMTU路径发现等初始化或更新;上述流程完毕后调用tcp_finish_connect完成连接建立,将连接状态更新为TCP_ESTABLISHED,若需要则开启保活定时器,以及对是否走快速路径做判断和标记;连接建立成功会进行一些唤醒操作;最后进行ACK模式是否切换的判断和操作,决定如何回复ACK;

  1 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
  2                      const struct tcphdr *th)
  3 {
  4     struct inet_connection_sock *icsk = inet_csk(sk);
  5     struct tcp_sock *tp = tcp_sk(sk);
  6     struct tcp_fastopen_cookie foc = { .len = -1 };
  7     int saved_clamp = tp->rx_opt.mss_clamp;
  8     bool fastopen_fail;
  9 
 10     /* 解析tcp选项 */
 11     tcp_parse_options(skb, &tp->rx_opt, 0, &foc);
 12 
 13     /* 有时间戳,有回显,记录回显 */
 14     if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
 15         tp->rx_opt.rcv_tsecr -= tp->tsoffset;
 16 
 17     if (th->ack) {
 18         /* rfc793:
 19          * "If the state is SYN-SENT then
 20          *    first check the ACK bit
 21          *      If the ACK bit is set
 22          *      If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send
 23          *        a reset (unless the RST bit is set, if so drop
 24          *        the segment and return)"
 25          */
 26         /* ack <= una || ack > nxt,ack确认序号错误 */
 27         if (!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_una) ||
 28             after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))
 29             goto reset_and_undo;
 30 
 31         /* 对端时间戳不合法 */
 32         if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
 33             !between(tp->rx_opt.rcv_tsecr, tp->retrans_stamp,
 34                  tcp_time_stamp)) {
 35             NET_INC_STATS(sock_net(sk),
 36                     LINUX_MIB_PAWSACTIVEREJECTED);
 37             goto reset_and_undo;
 38         }
 39 
 40         /* Now ACK is acceptable.
 41          *
 42          * "If the RST bit is set
 43          *    If the ACK was acceptable then signal the user "error:
 44          *    connection reset", drop the segment, enter CLOSED state,
 45          *    delete TCB, and return."
 46          */
 47 
 48         /* 设置了rst标志 */
 49         if (th->rst) {
 50             tcp_reset(sk);
 51             goto discard;
 52         }
 53 
 54         /* rfc793:
 55          *   "fifth, if neither of the SYN or RST bits is set then
 56          *    drop the segment and return."
 57          *
 58          *    See note below!
 59          *                                        --ANK(990513)
 60          */
 61         /* 未设置syn标志 */
 62         if (!th->syn)
 63             goto discard_and_undo;
 64 
 65         /* rfc793:
 66          *   "If the SYN bit is on ...
 67          *    are acceptable then ...
 68          *    (our SYN has been ACKed), change the connection
 69          *    state to ESTABLISHED..."
 70          */
 71         /* ecn标记 */
 72         tcp_ecn_rcv_synack(tp, th);
 73 
 74         /* 记录窗口更新时数据包序号 */
 75         tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
 76 
 77         //输入ack处理,使用慢速路径,可能会更新发送窗口
 78         tcp_ack(sk, skb, FLAG_SLOWPATH);
 79 
 80         /* Ok.. it's good. Set up sequence numbers and
 81          * move to established.
 82          */
 83 
 84         /* 更新下一个要接收的序号 */
 85         tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
 86         /* 更新窗口左边,即窗口中最小的序号 */
 87         tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
 88 
 89         /* RFC1323: The window in SYN & SYN/ACK segments is
 90          * never scaled.
 91          */
 92         /* 获取发送窗口 */
 93         tp->snd_wnd = ntohs(th->window);
 94 
 95         /* 没有窗口扩大因子 */
 96         if (!tp->rx_opt.wscale_ok) {
 97             /* 设置为0 */
 98             tp->rx_opt.snd_wscale = tp->rx_opt.rcv_wscale = 0;
 99             /* 设置最大值 */
100             tp->window_clamp = min(tp->window_clamp, 65535U);
101         }
102 
103         /* 有时间戳选项 */
104         if (tp->rx_opt.saw_tstamp) {
105             /* 在syn中有时间戳选项 */
106             tp->rx_opt.tstamp_ok       = 1;
107 
108             /* tcp首部需要增加时间戳长度 */
109             tp->tcp_header_len =
110                 sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
111 
112             /* mss需要减去时间戳长度 */
113             tp->advmss        -= TCPOLEN_TSTAMP_ALIGNED;
114 
115             /* 设置回显时间戳 */
116             tcp_store_ts_recent(tp);
117         } else {
118             /* 记录tcp首部长度 */
119             tp->tcp_header_len = sizeof(struct tcphdr);
120         }
121 
122         /* 有sack选项,开启了fack算法,则打标记 */
123         if (tcp_is_sack(tp) && sysctl_tcp_fack)
124             tcp_enable_fack(tp);
125 
126         /* MTU探测相关初始化 */
127         tcp_mtup_init(sk);
128         /* 计算mss */
129         tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
130         /* 初始化rcv_mss */
131         tcp_initialize_rcv_mss(sk);
132 
133         /* Remember, tcp_poll() does not lock socket!
134          * Change state from SYN-SENT only after copied_seq
135          * is initialized. */
136 
137         /* 记录用户空间待读取的序号 */
138         tp->copied_seq = tp->rcv_nxt;
139 
140         smp_mb();
141 
142         /* 连接建立完成的状态改变和相关初始化 */
143         tcp_finish_connect(sk, skb);
144 
145         /*  fastopen处理 */
146         fastopen_fail = (tp->syn_fastopen || tp->syn_data) &&
147                 tcp_rcv_fastopen_synack(sk, skb, &foc);
148 
149         /* 连接未在关闭态,则进行一些唤醒操作 */
150         if (!sock_flag(sk, SOCK_DEAD)) {
151             sk->sk_state_change(sk);
152             sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
153         }
154         if (fastopen_fail)
155             return -1;
156 
157         /* 有写数据请求|| 收包accept  || 延迟ack */
158         if (sk->sk_write_pending ||
159             icsk->icsk_accept_queue.rskq_defer_accept ||
160             icsk->icsk_ack.pingpong) {
161             /* Save one ACK. Data will be ready after
162              * several ticks, if write_pending is set.
163              *
164              * It may be deleted, but with this feature tcpdumps
165              * look so _wonderfully_ clever, that I was not able
166              * to stand against the temptation 8)     --ANK
167              */
168             /* 标志ack调度 */
169             inet_csk_schedule_ack(sk);
170             /* 进入快速ack模式 */
171             tcp_enter_quickack_mode(sk);
172             /* 设置延迟ack定时器 */
173             inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
174                           TCP_DELACK_MAX, TCP_RTO_MAX);
175 
176 discard:
177             tcp_drop(sk, skb);
178             return 0;
179         } else {
180             /*  回复ack */
181             tcp_send_ack(sk);
182         }
183         return -1;
184     }
185 
186         /* 省略了rst以及syn状态的代码 */
187 }

 

以上流程中比较重要的两个函数为tcp_ack与tcp_finish_connect;

tcp_ack函数因为涉及内容较多,此处不做分析,后续单独分析后在补充链接;

 

tcp_finish_connect函数进行连接建立完成的处理,函数将连接状态更改为TCP_ESTABLISHED,检查并重建路由项,初始化拥塞控制,若需要则开启保活定时器,以及对是否走快速路径做判断和标记

 1 /* tcp连接建立完成 */
 2 void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
 3 {
 4     struct tcp_sock *tp = tcp_sk(sk);
 5     struct inet_connection_sock *icsk = inet_csk(sk);
 6 
 7     /* 设置为已连接状态 */
 8     tcp_set_state(sk, TCP_ESTABLISHED);
 9     /* 记录最后一次收到包的时间戳 */
10     icsk->icsk_ack.lrcvtime = tcp_time_stamp;
11 
12     if (skb) {
13         /* 设置接收路由缓存 */
14         icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
15         security_inet_conn_established(sk, skb);
16     }
17 
18     /* Make sure socket is routed, for correct metrics.  */
19     /* 检查或重建路由 */
20     icsk->icsk_af_ops->rebuild_header(sk);
21 
22     /* 初始化度量值 */
23     tcp_init_metrics(sk);
24 
25     /* 初始化拥塞控制 */
26     tcp_init_congestion_control(sk);
27 
28     /* Prevent spurious tcp_cwnd_restart() on first data
29      * packet.
30      */
31     /* 记录最后一次发送数据包的时间 */
32     tp->lsndtime = tcp_time_stamp;
33 
34     tcp_init_buffer_space(sk);
35 
36     /* 开启了保活,则打开保活定时器 */
37     if (sock_flag(sk, SOCK_KEEPOPEN))
38         inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp));
39 
40     /* 设置预测标志,判断快慢路径的条件之一 */
41     if (!tp->rx_opt.snd_wscale)
42         __tcp_fast_path_on(tp, tp->snd_wnd);
43     else
44         tp->pred_flags = 0;
45 
46 }

 

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-06-30
  • 2021-11-23
  • 2021-09-18
  • 2022-12-23
  • 2021-09-01
  • 2021-06-05
猜你喜欢
  • 2021-08-01
  • 2022-02-05
  • 2021-10-04
  • 2022-12-23
  • 2021-06-17
  • 2022-12-23
相关资源
相似解决方案