TCP三步握手建立连接(1)-----主动连接syn包发送

来源:互联网 发布:网络骗局有哪些 编辑:程序博客网 时间:2024/05/24 01:41

TCP连接的建立一般是服务器端进入监听状态,然后客户端调用connect来连接。本文分析connect调用的过程,也即第一个syn报文的发送。

 

connect对应的系统调用是sys_connect

[cpp] view plaincopy
  1. SYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,  
  2.         int, addrlen)  
  3. {  
  4.     struct socket *sock;  
  5.     struct sockaddr_storage address;  
  6.     int err, fput_needed;  
  7.         /* 找到文件描述符对应的BSD socket结构,在前面的socket调用中建立*/  
  8.     sock = sockfd_lookup_light(fd, &err, &fput_needed);  
  9.     if (!sock)  
  10.         goto out;  
  11.         /* copy对端的地址到内核空间 */  
  12.     err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address);  
  13.     if (err < 0)  
  14.         goto out_put;  
  15.   
  16.     err =  
  17.         security_socket_connect(sock, (struct sockaddr *)&address, addrlen);  
  18.     if (err)  
  19.         goto out_put;  
  20.         /* 调用该BSD socket对应的connect调用 */  
  21.     err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,  
  22.                  sock->file->f_flags);  
  23. out_put:  
  24.         /* 释放文件的引用 */  
  25.     fput_light(sock->file, fput_needed);  
  26. out:  
  27.     return err;  

 

上面函数中的sock是在前面的socket调用的时候就初始化的,对应于TCP的BSD socket 的操作符集是inet_stream_ops

注:网络子模块在内核初始化的时候就注册了TCP,UDP和RAW3中协议,具体在af_inet.c中的inet_init函数中。

[cpp] view plaincopy
  1. onst struct proto_ops inet_stream_ops = {  
  2.     .family        = PF_INET,  
  3.     .owner         = THIS_MODULE,  
  4.     .release       = inet_release,  
  5.     .bind          = inet_bind,  
  6.     .connect       = inet_stream_connect,  
  7.     .socketpair    = sock_no_socketpair,  
  8.     .accept        = inet_accept,  
  9.     .getname       = inet_getname,  
  10.     .poll          = tcp_poll,  
  11.     .ioctl         = inet_ioctl,  
  12.     .listen        = inet_listen,  
  13.     .shutdown      = inet_shutdown,  
  14.     .setsockopt    = sock_common_setsockopt,  
  15.     .getsockopt    = sock_common_getsockopt,  
  16.     .sendmsg       = tcp_sendmsg,  
  17.     .recvmsg       = sock_common_recvmsg,  
  18.     .mmap          = sock_no_mmap,  
  19.     .sendpage      = tcp_sendpage,  
  20.     .splice_read       = tcp_splice_read,  
  21. #ifdef CONFIG_COMPAT  
  22.     .compat_setsockopt = compat_sock_common_setsockopt,  
  23.     .compat_getsockopt = compat_sock_common_getsockopt,  
  24. #endif  
  25. };  

从上面的结构中可以看到connect对应的函数是inet_stream_connect。下面分析该函数

[c-sharp] view plaincopy
  1. /* 
  2.  *  Connect to a remote host. There is regrettably still a little 
  3.  *  TCP 'magic' in here. 
  4.  */  
  5. int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,  
  6.             int addr_len, int flags)  
  7. {  
  8.     struct sock *sk = sock->sk;  
  9.     int err;  
  10.     long timeo;  
  11.   
  12.     lock_sock(sk);  
  13.   
  14.     if (uaddr->sa_family == AF_UNSPEC) {  
  15.         err = sk->sk_prot->disconnect(sk, flags);  
  16.         sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;  
  17.         goto out;  
  18.     }  
  19.   
  20.     switch (sock->state) {  
  21.     default:  
  22.         err = -EINVAL;  
  23.         goto out;  
  24.     case SS_CONNECTED:     /* 该BSD socket已连接*/  
  25.         err = -EISCONN;  
  26.         goto out;  
  27.     case SS_CONNECTING:   /* 该BSD socket正在连接*/  
  28.         err = -EALREADY;  
  29.         /* Fall out of switch with err, set for this state */  
  30.         break;  
  31.     case SS_UNCONNECTED:  
  32.         err = -EISCONN;  
  33.         if (sk->sk_state != TCP_CLOSE)  
  34.             goto out;  
  35.                 /* INET SOCKET 调用协议特有connect操作符 */  
  36.         err = sk->sk_prot->connect(sk, uaddr, addr_len);  
  37.         if (err < 0)  
  38.             goto out;  
  39.                 /* 上面的调用完成后,连接并没有完成,*/  
  40.         sock->state = SS_CONNECTING;  
  41.   
  42.         /* Just entered SS_CONNECTING state; the only 
  43.          * difference is that return value in non-blocking 
  44.          * case is EINPROGRESS, rather than EALREADY. 
  45.          */  
  46.         err = -EINPROGRESS;  
  47.         break;  
  48.     }  
  49.         /* 获取连接超时时间*/  
  50.     timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);  
  51.   
  52.     if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {  
  53.         /* Error code is set above 进入定时等待 */  
  54.         if (!timeo || !inet_wait_for_connect(sk, timeo))  
  55.             goto out;  
  56.   
  57.         err = sock_intr_errno(timeo);  
  58.         if (signal_pending(current))  
  59.             goto out;  
  60.     }  
  61.   
  62.     /* Connection was closed by RST, timeout, ICMP error 
  63.      * or another process disconnected us. 
  64.      */  
  65.     if (sk->sk_state == TCP_CLOSE)  
  66.         goto sock_error;  
  67.   
  68.     /* sk->sk_err may be not zero now, if RECVERR was ordered by user 
  69.      * and error was received after socket entered established state. 
  70.      * Hence, it is handled normally after connect() return successfully. 
  71.      */  
  72.   
  73.     sock->state = SS_CONNECTED;  
  74.     err = 0;  
  75. out:  
  76.     release_sock(sk);  
  77.     return err;  
  78.   
  79. sock_error:  
  80.     err = sock_error(sk) ? : -ECONNABORTED;  
  81.     sock->state = SS_UNCONNECTED;  
  82.     if (sk->sk_prot->disconnect(sk, flags))  
  83.         sock->state = SS_DISCONNECTING;  
  84.     goto out;  
  85. }  

 

对于INET socket中的tcp连接,协议特有操作符集为tcp_prot

[cpp] view plaincopy
  1. struct proto tcp_prot = {  
  2.     .name           = "TCP",  
  3.     .owner          = THIS_MODULE,  
  4.     .close          = tcp_close,  
  5.     .connect        = tcp_v4_connect,  
  6.     .disconnect     = tcp_disconnect,  
  7.     .accept         = inet_csk_accept,  
  8.     .ioctl          = tcp_ioctl,  
  9.     .init           = tcp_v4_init_sock,  
  10.     .destroy        = tcp_v4_destroy_sock,  
  11.     .shutdown       = tcp_shutdown,  
  12.     .setsockopt     = tcp_setsockopt,  
  13.     .getsockopt     = tcp_getsockopt,  
  14.     .recvmsg        = tcp_recvmsg,  
  15.     .backlog_rcv        = tcp_v4_do_rcv,  
  16.     .hash           = inet_hash,  
  17.     .unhash         = inet_unhash,  
  18.     .get_port       = inet_csk_get_port,  
  19.     .enter_memory_pressure  = tcp_enter_memory_pressure,  
  20.     .sockets_allocated  = &tcp_sockets_allocated,  
  21.     .orphan_count       = &tcp_orphan_count,  
  22.     .memory_allocated   = &tcp_memory_allocated,  
  23.     .memory_pressure    = &tcp_memory_pressure,  
  24.     .sysctl_mem     = sysctl_tcp_mem,  
  25.     .sysctl_wmem        = sysctl_tcp_wmem,  
  26.     .sysctl_rmem        = sysctl_tcp_rmem,  
  27.     .max_header     = MAX_TCP_HEADER,  
  28.     .obj_size       = sizeof(struct tcp_sock),  
  29.     .slab_flags     = SLAB_DESTROY_BY_RCU,  
  30.     .twsk_prot      = &tcp_timewait_sock_ops,  
  31.     .rsk_prot       = &tcp_request_sock_ops,  
  32.     .h.hashinfo     = &tcp_hashinfo,  
  33. #ifdef CONFIG_COMPAT  
  34.     .compat_setsockopt  = compat_tcp_setsockopt,  
  35.     .compat_getsockopt  = compat_tcp_getsockopt,  
  36. #endif  
  37. };  

 

可以看出这2个操作符集中好多函数是相同的,不过它们是出于不同层次中的,从系统调用的角度来看,BSD socket的操作符集是先

被调用的,然后再调用对应的INET socket的操作符集。

对于TCP,流程进入tcp_v4_connect函数

[cpp] view plaincopy
  1. /* This will initiate an outgoing connection. */  
  2. int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)  
  3. {  
  4.     struct inet_sock *inet = inet_sk(sk);  
  5.     struct tcp_sock *tp = tcp_sk(sk);  
  6.     struct sockaddr_in *usin = (struct sockaddr_in *)uaddr;  
  7.     struct rtable *rt;  
  8.     __be32 daddr, nexthop;  
  9.     int tmp;  
  10.     int err;  
  11.   
  12.     if (addr_len < sizeof(struct sockaddr_in))  
  13.         return -EINVAL;  
  14.   
  15.     if (usin->sin_family != AF_INET)  
  16.         return -EAFNOSUPPORT;  
  17.         /* 开始准备路由 */  
  18.     nexthop = daddr = usin->sin_addr.s_addr;  
  19.     if (inet->opt && inet->opt->srr) {  
  20.         if (!daddr)  
  21.             return -EINVAL;  
  22.         nexthop = inet->opt->faddr;  
  23.     }  
  24.         /* 调用路由模块获取出口信息,这里不深入 */  
  25.     tmp = ip_route_connect(&rt, nexthop, inet->saddr,  
  26.                    RT_CONN_FLAGS(sk), sk->sk_bound_dev_if,  
  27.                    IPPROTO_TCP,  
  28.                    inet->sport, usin->sin_port, sk, 1);  
  29.     if (tmp < 0) {  
  30.         if (tmp == -ENETUNREACH)  
  31.             IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);  
  32.         return tmp;  
  33.     }  
  34.         /* 如果获取的路由是广播或多播域, 返回网络不可达,tcp不支持多播与广播 */  
  35.     if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {  
  36.         ip_rt_put(rt);  
  37.         return -ENETUNREACH;  
  38.     }  
  39.   
  40.     if (!inet->opt || !inet->opt->srr)  
  41.         daddr = rt->rt_dst;  
  42.   
  43.     if (!inet->saddr)  
  44.         inet->saddr = rt->rt_src;  
  45.     inet->rcv_saddr = inet->saddr;  
  46.         
  47.     if (tp->rx_opt.ts_recent_stamp && inet->daddr != daddr) {  
  48.         /* Reset inherited state */  
  49.         tp->rx_opt.ts_recent    = 0;  
  50.         tp->rx_opt.ts_recent_stamp = 0;  
  51.         tp->write_seq           = 0;  
  52.     }  
  53.   
  54.     if (tcp_death_row.sysctl_tw_recycle &&  
  55.         !tp->rx_opt.ts_recent_stamp && rt->rt_dst == daddr) {  
  56.         struct inet_peer *peer = rt_get_peer(rt);  
  57.         /* 
  58.          * VJ's idea. We save last timestamp seen from 
  59.          * the destination in peer table, when entering state 
  60.          * TIME-WAIT * and initialize rx_opt.ts_recent from it, 
  61.          * when trying new connection. 
  62.          */  
  63.         if (peer != NULL &&  
  64.             peer->tcp_ts_stamp + TCP_PAWS_MSL >= get_seconds()) {  
  65.             tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;  
  66.             tp->rx_opt.ts_recent = peer->tcp_ts;  
  67.         }  
  68.     }  
  69.   
  70.     inet->dport = usin->sin_port;  
  71.     inet->daddr = daddr;  
  72.   
  73.     inet_csk(sk)->icsk_ext_hdr_len = 0;  
  74.     if (inet->opt)  
  75.         inet_csk(sk)->icsk_ext_hdr_len = inet->opt->optlen;  
  76.         /* mss_clamp */  
  77.     tp->rx_opt.mss_clamp = 536;  
  78.   
  79.     /* Socket identity is still unknown (sport may be zero). 
  80.      * However we set state to SYN-SENT and not releasing socket 
  81.      * lock select source port, enter ourselves into the hash tables and 
  82.      * complete initialization after this. 
  83.      */  
  84.     tcp_set_state(sk, TCP_SYN_SENT);  
  85.     err = inet_hash_connect(&tcp_death_row, sk);  
  86.     if (err)  
  87.         goto failure;  
  88.   
  89.     err = ip_route_newports(&rt, IPPROTO_TCP,  
  90.                 inet->sport, inet->dport, sk);  
  91.     if (err)  
  92.         goto failure;  
  93.   
  94.     /* OK, now commit destination to socket.  */  
  95.     sk->sk_gso_type = SKB_GSO_TCPV4;  
  96.     sk_setup_caps(sk, &rt->u.dst);  
  97.   
  98.     if (!tp->write_seq)  
  99.         tp->write_seq = secure_tcp_sequence_number(inet->saddr,  
  100.                                inet->daddr,  
  101.                                inet->sport,  
  102.                                usin->sin_port);  
  103.         /* id是IP包头的id域 */  
  104.     inet->id = tp->write_seq ^ jiffies;  
  105.   
  106.     err = tcp_connect(sk);  
  107.     rt = NULL;  
  108.     if (err)  
  109.         goto failure;  
  110.   
  111.     return 0;  
  112.   
  113. failure:  
  114.     /* 
  115.      * This unhashes the socket and releases the local port, 
  116.      * if necessary. 
  117.      */  
  118.     tcp_set_state(sk, TCP_CLOSE);  
  119.     ip_rt_put(rt);  
  120.     sk->sk_route_caps = 0;  
  121.     inet->dport = 0;  
  122.     return err;  
  123. }  

该函数的主要功能是准备好路由,如果源端口没有指定,还要选择一个端口,然后再次更新路由信息;代表该连接的sk结构加入

到对应的hash表中(已连接ehash)。获取一个write_seq,以及当 sysctl_tw_recycle设置时,读取上次连接(如果存在)

进入time-wait时保存的时戳,赋给当前连接的rx_opt结构中。

 

最后该函数调用tcp_connect来完成连接

[cpp] view plaincopy
  1. /* 
  2.  * Build a SYN and send it off. 
  3.  */  
  4. int tcp_connect(struct sock *sk)  
  5. {  
  6.     struct tcp_sock *tp = tcp_sk(sk);  
  7.     struct sk_buff *buff;  
  8.         /* 初始化连接对应的INET socket结构的参数,为连接做准备 */  
  9.     tcp_connect_init(sk);  
  10.         /* 获取一个skb,由于是syn包,没有数据,所以大小是MAX_TCP_HEADER的16位对齐 */  
  11.     buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);  
  12.     if (unlikely(buff == NULL))  
  13.         return -ENOBUFS;  
  14.   
  15.     /* Reserve space for headers. */  
  16.     skb_reserve(buff, MAX_TCP_HEADER);  
  17.           
  18.     tp->snd_nxt = tp->write_seq;  
  19.         /* 设置skb相关参数 */  
  20.     tcp_init_nondata_skb(buff, tp->write_seq++, TCPCB_FLAG_SYN);  
  21.         /* 设置ECN */  
  22.     TCP_ECN_send_syn(sk, buff);  
  23.   
  24.     /* Send it off. */  
  25.         /* 保存该数据包的发送时间*/  
  26.     TCP_SKB_CB(buff)->when = tcp_time_stamp;  
  27.     tp->retrans_stamp = TCP_SKB_CB(buff)->when;  
  28.     skb_header_release(buff);  
  29.         /* 加入发送队列,待确认后在丢弃*/  
  30.     __tcp_add_write_queue_tail(sk, buff);  
  31.     sk->sk_wmem_queued += buff->truesize;  
  32.     sk_mem_charge(sk, buff->truesize);  
  33.     tp->packets_out += tcp_skb_pcount(buff);  
  34.     tcp_transmit_skb(sk, buff, 1, GFP_KERNEL);  
  35.   
  36.     /* We change tp->snd_nxt after the tcp_transmit_skb() call 
  37.      * in order to make this packet get counted in tcpOutSegs. 
  38.      */  
  39.     tp->snd_nxt = tp->write_seq;  
  40.     tp->pushed_seq = tp->write_seq;  
  41.     TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);  
  42.   
  43.     /* Timer for repeating the SYN until an answer. */  
  44.     inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,  
  45.                   inet_csk(sk)->icsk_rto, TCP_RTO_MAX);  
  46.     return 0;  
  47. }  

 

该函数的流程是

  1. 初始化该次连接的sk结构
  2. 分配一个skb数据包
  3. 初始化skb
  4. skb加入发送队列后,调用tcp_transmit_skb发送该数据包
  5. 更新snd_nxt,并启动超时定时器。

首先来看sk结构的初始化,也就是tcp_connect_init函数

[cpp] view plaincopy
  1. /* 
  2.  * Do all connect socket setups that can be done AF independent. 
  3.  */  
  4. static void tcp_connect_init(struct sock *sk)  
  5. {  
  6.     struct dst_entry *dst = __sk_dst_get(sk);  
  7.     struct tcp_sock *tp = tcp_sk(sk);  
  8.     __u8 rcv_wscale;  
  9.   
  10.     /* We'll fix this up when we get a response from the other end. 
  11.      * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT. 
  12.      */  
  13.     tp->tcp_header_len = sizeof(struct tcphdr) +  
  14.         (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);  
  15.   
  16. #ifdef CONFIG_TCP_MD5SIG  
  17.     if (tp->af_specific->md5_lookup(sk, sk) != NULL)  
  18.         tp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;  
  19. #endif  
  20.   
  21.     /* If user gave his TCP_MAXSEG, record it to clamp */  
  22.     if (tp->rx_opt.user_mss)  
  23.         tp->rx_opt.mss_clamp = tp->rx_opt.user_mss;  
  24.     tp->max_window = 0;  
  25.         /* 初始化MTU probe*/  
  26.     tcp_mtup_init(sk);  
  27.         /* 设置mss */  
  28.     tcp_sync_mss(sk, dst_mtu(dst));  
  29.   
  30.     if (!tp->window_clamp)  
  31.         tp->window_clamp = dst_metric(dst, RTAX_WINDOW);  
  32.     tp->advmss = dst_metric(dst, RTAX_ADVMSS);  
  33.     if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < tp->advmss)  
  34.         tp->advmss = tp->rx_opt.user_mss;  
  35.   
  36.     tcp_initialize_rcv_mss(sk);  
  37.         /* 根据接收空间大小初始化一个通告窗口 */  
  38.     tcp_select_initial_window(tcp_full_space(sk),  
  39.                   tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),  
  40.                   &tp->rcv_wnd,  
  41.                   &tp->window_clamp,  
  42.                   sysctl_tcp_window_scaling,  
  43.                   &rcv_wscale);  
  44.   
  45.     tp->rx_opt.rcv_wscale = rcv_wscale;  
  46.     tp->rcv_ssthresh = tp->rcv_wnd;  
  47.   
  48.     sk->sk_err = 0;  
  49.     sock_reset_flag(sk, SOCK_DONE);  
  50.     tp->snd_wnd = 0;  
  51.         /* 更新一些滑动窗口的成员*/  
  52.     tcp_init_wl(tp, tp->write_seq, 0);  
  53.     tp->snd_una = tp->write_seq;  
  54.     tp->snd_sml = tp->write_seq;  
  55.     tp->snd_up = tp->write_seq;  
  56.     tp->rcv_nxt = 0;  
  57.     tp->rcv_wup = 0;  
  58.     tp->copied_seq = 0;  
  59.   
  60.     inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT;  
  61.     inet_csk(sk)->icsk_retransmits = 0;  
  62.     tcp_clear_retrans(tp);  
  63. }  

 

接下来发送数据包,调用tcp_transimit_skb函数

[cpp] view plaincopy
  1. /* This routine actually transmits TCP packets queued in by 
  2.  * tcp_do_sendmsg().  This is used by both the initial 
  3.  * transmission and possible later retransmissions. 
  4.  * All SKB's seen here are completely headerless.  It is our 
  5.  * job to build the TCP header, and pass the packet down to 
  6.  * IP so it can do the same plus pass the packet off to the 
  7.  * device. 
  8.  * 该函数是TCP模块通用的发送函数,在此要负责组装TCP头部 
  9.  * We are working here with either a clone of the original 
  10.  * SKB, or a fresh unique copy made by the retransmit engine. 
  11.  */  
  12.   
  13. static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,  
  14.                 gfp_t gfp_mask)  
  15. {  
  16.     const struct inet_connection_sock *icsk = inet_csk(sk);  
  17.     struct inet_sock *inet;  
  18.     struct tcp_sock *tp;  
  19.     struct tcp_skb_cb *tcb;  
  20.     struct tcp_out_options opts;  
  21.     unsigned tcp_options_size, tcp_header_size;  
  22.     struct tcp_md5sig_key *md5;  
  23.     __u8 *md5_hash_location;  
  24.     struct tcphdr *th;  
  25.     int err;  
  26.   
  27.     BUG_ON(!skb || !tcp_skb_pcount(skb));  
  28.   
  29.     /* If congestion control is doing timestamping, we must 
  30.      * take such a timestamp before we potentially clone/copy. 
  31.      */  
  32.     if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP)  
  33.         __net_timestamp(skb);  
  34.         /* 因为发送时要保留skb以备重传,所以大部分时候都设置了clone_it,不过发送ack 除外*/  
  35.     if (likely(clone_it)) {  
  36.         if (unlikely(skb_cloned(skb)))  
  37.             skb = pskb_copy(skb, gfp_mask);  
  38.         else  
  39.             skb = skb_clone(skb, gfp_mask);  
  40.         if (unlikely(!skb))  
  41.             return -ENOBUFS;  
  42.     }  
  43.   
  44.     inet = inet_sk(sk);  
  45.     tp = tcp_sk(sk);  
  46.     tcb = TCP_SKB_CB(skb);  
  47.     memset(&opts, 0, sizeof(opts));  
  48.         /* 获取tcp选项部分信息,分为syn包和普通包2部分,因为有的选项只在syn中设置*/  
  49.     if (unlikely(tcb->flags & TCPCB_FLAG_SYN))  
  50.         tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5);  
  51.     else  
  52.         tcp_options_size = tcp_established_options(sk, skb, &opts,  
  53.                                &md5);  
  54.     tcp_header_size = tcp_options_size + sizeof(struct tcphdr);  
  55.         /* 该函数判断是否网络中有数据存在,没有就通知拥塞控制模块 */  
  56.     if (tcp_packets_in_flight(tp) == 0)  
  57.         tcp_ca_event(sk, CA_EVENT_TX_START);  
  58.   
  59.     skb_push(skb, tcp_header_size);  
  60.     skb_reset_transport_header(skb);  
  61.     skb_set_owner_w(skb, sk);  
  62.   
  63.     /* Build TCP header and checksum it. */  
  64.     th = tcp_hdr(skb);  
  65.     th->source       = inet->sport;  
  66.     th->dest     = inet->dport;  
  67.     th->seq          = htonl(tcb->seq);  
  68.     th->ack_seq      = htonl(tp->rcv_nxt);  
  69.     *(((__be16 *)th) + 6)   = htons(((tcp_header_size >> 2) << 12) |  
  70.                     tcb->flags);  
  71.   
  72.     if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) {  
  73.         /* RFC1323: The window in SYN & SYN/ACK segments 
  74.          * is never scaled.SYN包和SYN/ACK包中窗口不扩放 
  75.          */  
  76.         th->window   = htons(min(tp->rcv_wnd, 65535U));  
  77.     } else {  
  78.         th->window   = htons(tcp_select_window(sk));  
  79.     }  
  80.     th->check        = 0;  
  81.     th->urg_ptr      = 0;  
  82.   
  83.     /* The urg_mode check is necessary during a below snd_una win probe */  
  84.     if (unlikely(tcp_urg_mode(tp) &&  
  85.              between(tp->snd_up, tcb->seq + 1, tcb->seq + 0xFFFF))) {  
  86.         th->urg_ptr      = htons(tp->snd_up - tcb->seq);  
  87.         th->urg          = 1;  
  88.     }  
  89.         /* 填充TCP 选项字段 */  
  90.     tcp_options_write((__be32 *)(th + 1), tp, &opts, &md5_hash_location);  
  91.     if (likely((tcb->flags & TCPCB_FLAG_SYN) == 0))  
  92.                 /* 如果不是SYN包的话,就检查是否需要向对方发送ECN */  
  93.         TCP_ECN_send(sk, skb, tcp_header_size);  
  94.   
  95. #ifdef CONFIG_TCP_MD5SIG  
  96.     /* Calculate the MD5 hash, as we have all we need now */  
  97.     if (md5) {  
  98.         sk->sk_route_caps &= ~NETIF_F_GSO_MASK;  
  99.         tp->af_specific->calc_md5_hash(md5_hash_location,  
  100.                            md5, sk, NULL, skb);  
  101.     }  
  102. #endif  
  103.         /* 校验和相关计算*/  
  104.     icsk->icsk_af_ops->send_check(sk, skb->len, skb);  
  105.   
  106.     if (likely(tcb->flags & TCPCB_FLAG_ACK))  
  107.                 /* 如果发送了ACK的话,就。。。*/  
  108.         tcp_event_ack_sent(sk, tcp_skb_pcount(skb));  
  109.   
  110.     if (skb->len != tcp_header_size)  
  111.                  /* 如果发送了数据的话,就。。。*/  
  112.         tcp_event_data_sent(tp, skb, sk);  
  113.   
  114.     if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)  
  115.         TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);  
  116.         /* 调用IP层的发送函数 */  
  117.     err = icsk->icsk_af_ops->queue_xmit(skb, 0);  
  118.     if (likely(err <= 0))  
  119.         return err;  
  120.   
  121.     tcp_enter_cwr(sk, 1);  
  122.   
  123.     return net_xmit_eval(err);  
  124. }  

 

skb发送后,connect并没有返回,因为此时连接还没有建立,tcp进入等待状态,此时回到前面的inet_stream_connect函数

在发送syn后进入等待状态

[cpp] view plaincopy
  1. static long inet_wait_for_connect(struct sock *sk, long timeo)  
  2. {  
  3.     DEFINE_WAIT(wait);  
  4.         /* sk_sleep 保存此INET SOCKET的等待队列 */  
  5.     prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);  
  6.   
  7.     /* Basic assumption: if someone sets sk->sk_err, he _must_ 
  8.      * change state of the socket from TCP_SYN_*. 
  9.      * Connect() does not allow to get error notifications 
  10.      * without closing the socket. 
  11.      */  
  12.         /* 定时等待知道状态变化 */  
  13.     while ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {  
  14.         release_sock(sk);  
  15.         timeo = schedule_timeout(timeo);  
  16.         lock_sock(sk);  
  17.         if (signal_pending(current) || !timeo)  
  18.             break;  
  19.         prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);  
  20.     }  
  21.     finish_wait(sk->sk_sleep, &wait);  
  22.     return timeo;  
  23. }  

 

下文中将分析第二步:服务器端返回syn_ack包