本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径。
内核版本:3.6
Author:zhangskd @ csdn blog
函数路径
以下是第三次握手时,服务端接收到ACK后的处理路径。
接收入口
1. 状态为ESTABLISHED时,用tcp_rcv_established()接收处理。
2. 状态为LISTEN时,说明这个sock处于监听状态,用于被动打开的接收处理,包括SYN和ACK。
3. 当状态不为ESTABLISHED或TIME_WAIT时,用tcp_rcv_state_process()处理。
inttcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
struct sock *rsk;
#ifdef CONFIG_TCP_MD5SIG
/* We really want to reject the packet as early as possible if :
* We're expecting an MD5'd packet and this is no MD5 tcp option.
* There is an MD5 option and we're not expecting one.
*/
if(tcp_v4_inbound_md5_hash(sk, skb))
gotodiscard;
#endif
/* 当状态为ESTABLISHED时,用tcp_rcv_established()接收处理 */
if(sk->sk_state == TCP_ESTABLISHED) {/* Fast path */
struct dst_entry *dst = sk->sk_rx_dst;
sock_rps_save_rxhash(sk, skb);
if(dst) {
if(inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst,0) == NULL) {
dst_release(dst);
sk->sk_rx_dst = NULL;
}
}
/* 连接已建立时的处理路径 */
if(tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
rsk = sk;
gotoreset;
}
return0;
}
/* 检查报文长度、报文校验和 */
if(skb->len
gotocsum_err;
/* 如果这个sock处于监听状态,被动打开时的处理,包括收到SYN或ACK */
if(sk->sk_state == TCP_LISTEN) {
/* 返回值:
* NULL,错误
* nsk == sk,接收到SYN
* nsk != sk,接收到ACK
*/
struct sock *nsk = tcp_v4_hnd_req(sk, skb); /* 接收ACK的处理 */
if(! nsk)
gotodiscard;
if(nsk != sk) {/* 接收到ACK时 */
sock_rps_save_rxhash(nsk, skb);
if(tcp_child_process(sk, nsk, skb)) {/* 处理新的sock */
rsk = nsk;
gotoreset;
}
return0;
}
} else
sock_rps_save_rx(sk, skb);
/* 处理除了ESTABLISHED和TIME_WAIT之外的所有状态 */
if(tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
rsk = sk;
gotoreset;
}
return0;
reset:
tcp_v4_send_reset(rsk, skb); /* 发送RST包 */
discard:
kfree_skb(skb);
return0;
csum_err:
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
gotodiscard;
}
收到SYN段后,服务器端会分配一个连接请求块,并初始化这个连接请求块。
构造和发送SYNACK段。
然后把这个连接请求块链入半连接队列中,启动超时定时器。
之后如果再收到ACK,就能完成三次握手了。
staticstruct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
{
struct tcphdr *th = tcp_hdr(skb);
conststruct iphdr *iph = ip_hdr(skb);
struct sock *nsk;
struct request_sock **prev;
/* 在半连接队列中查找是否已有符合的连接请求块,如果有,则说明这是三次握手的最后一个ACK。*/
struct request_sock *req = inet_csk_search_req(sk, &prev, th->source, iph->saddr, iph->daddr);
if(req)
returntcp_check_req(sk, skb, req, prev);/* 服务器端处理三次握手的最后一个ACK */
/* 如果在半连接队列中没找到,则在ESTABLISHED状态的哈希表中查找。*/
nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr, th->source,
iph->daddr, th->dest, inet_iif(skb));
if(nsk) {/* 如果在ehash表中找到对应的sock,且不处于TIME_WAIT状态 */
if(nsk->sk_state != TCP_TIME_WAIT) {
bh_lock_sock(nsk);
returnnsk;
}
inet_twsk_put(ine