3.2 SYN的接收与SYN|ACK的发送

  客户端发送的SYN请求到达服务器的网卡后,进入服务器操作系统的网络协议栈,经过链路层和网络层的处理后,抵达TCP协议的入口函数。TCPv4的入口函数是tcp_v4_rcv,TCPv6的入口函数是tcp_v6_rcv。下面对tcp_v4_rcv进行分析:

 1961 int tcp_v4_rcv(struct sk_buff *skb)
1962 {   
1963     const struct iphdr *iph;
1964     const struct tcphdr *th;
1965     struct sock *sk;
1966     int ret;
...
1975     if (!pskb_may_pull(skb, sizeof(struct tcphdr))) 
1976         goto discard_it;
1977
1978     th = tcp_hdr(skb);
1979                     
1980     if (th->doff < sizeof(struct tcphdr) / 4)    //检查TCP头长度是否大于最小值
1981         goto bad_packet;
1982     if (!pskb_may_pull(skb, th->doff * 4))
1983         goto discard_it;
1984
1985     /* An explanation is required here, I think.
1986      * Packet length and doff are validated by header prediction,
1987      * provided case of th->doff==0 is eliminated.
1988      * So, we defer the checks. */
1989     if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))  //校验检验和
1990         goto csum_error;
1991
1992     th = tcp_hdr(skb);
1993     iph = ip_hdr(skb);
1994     TCP_SKB_CB(skb)->seq = ntohl(th->seq);
1995     TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
1996                     skb->len - th->doff * 4);
1997     TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
1998     TCP_SKB_CB(skb)->when    = 0;
1999     TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
2000     TCP_SKB_CB(skb)->sacked  = 0;
2001
2002     sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
2003     if (!sk)
2004         goto no_tcp_socket;
...
2023
2024     bh_lock_sock_nested(sk);    //加自旋锁,以防止其它软中断同时访问当前socket
2025     ret = 0;
2026     if (!sock_owned_by_user(sk)) {//如果没有进程锁定此socket;对于SYN此条件为真
...
2035         {
2036             if (!tcp_prequeue(sk, skb)) //判断是否加入到prequeue中;若无进程等待使用prequeue的话则调用tcp_v4_do_rcv函数进行处理
2037                 ret = tcp_v4_do_rcv(sk, skb);//进入主处理函数
2038         }
...
2043         goto discard_and_relse;
2044     }
2045     bh_unlock_sock(sk);
2046
2047     sock_put(sk);
2048
2049     return ret;
2050 
  代码解析:

  1975:pskb_may_pull的作用就是检测skb对应的主buf中是否有足够的空间来pull出len长度,如果不够就重新分配skb并将frags中的数据整合进连续空间中;SYN包则不存在frags中的数据
  1994-2000:将报文中的一些信息记录到skb的cb字段中以便使用;

  2002:__inet_lookup_skb会根据报文的源/目的IP和源/目的端口等信息在tcp_hashinfo中查找连接。先查找ehash,即established连接表,对于SYN请求这次查找会失败;然后再找listening_hash,这时会找到server端在listen系统调用中加入到listening_hash表中的socket。

  下面分析主处理函数tcp_v4_do_rcv:

1800 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) 
1801 {
1802     struct sock *rsk;    
...
1835     if (sk->sk_state == TCP_LISTEN) {
1836         struct sock *nsk = tcp_v4_hnd_req(sk, skb); //查找request sock
1837         if (!nsk)
1838             goto discard;
1839 
1840         if (nsk != sk) {
1841             sock_rps_save_rxhash(nsk, skb);
1842             if (tcp_child_process(sk, nsk, skb)) {
1843                 rsk = nsk;
1844                 goto reset;
1845             }
1846             return 0;
1847         }
1848     } else
1849         sock_rps_save_rxhash(sk, skb);
1850 
1851     if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
1852         rsk = sk;
1853         goto reset;
1854     }
1855     return 0;
...
  server端socket的状态一定是TCP_LISTEN,即1835行的判断成立。tcp_v4_hnd_req用于查找一个处于“半建立”状态(即发送SYN|ACK,等待ACK)的连接:

1739 static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_bu
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值