• (1) linux 3.x



    http://blog.csdn.net/zhangskd/article/details/16879781


    本文主要分析:服务器端接收到SYN包时的处理路径。

    内核版本:3.6

    Author:zhangskd @ csdn blog

    接收入口

    1. 状态为ESTABLISHED时,用tcp_rcv_established()接收处理。

    2. 状态为LISTEN时,说明这个sock处于监听状态,用于被动打开的接收处理,包括SYN和ACK。

    3. 当状态不为ESTABLISHED或TIME_WAIT时,用tcp_rcv_state_process()处理。

    1. int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)  
    2. {  
    3.     struct sock *rsk;  
    4.   
    5. #ifdef CONFIG_TCP_MD5SIG  
    6.     /* We really want to reject the packet as early as possible if : 
    7.      * We're expecting an MD5'd packet and this is no MD5 tcp option. 
    8.      * There is an MD5 option and we're not expecting one. 
    9.      */  
    10.     if (tcp_v4_inbound_md5_hash(sk, skb))  
    11.         goto discard;  
    12. #endif  
    13.   
    14.     /* 当状态为ESTABLISHED时,用tcp_rcv_established()接收处理 */  
    15.     if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */  
    16.         struct dst_entry *dst = sk->sk_rx_dst;  
    17.         sock_rps_save_rxhash(sk, skb);  
    18.   
    19.         if (dst) {  
    20.             if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst, 0) == NULL) {  
    21.                 dst_release(dst);  
    22.                 sk->sk_rx_dst = NULL;  
    23.             }  
    24.         }  
    25.    
    26.         /* 连接已建立时的处理路径 */  
    27.         if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {  
    28.             rsk = sk;  
    29.             goto reset;  
    30.         }  
    31.         return 0;  
    32.     }  
    33.   
    34.     /* 检查报文长度、报文校验和 */  
    35.     if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))  
    36.         goto csum_err;  
    37.   
    38.     /* 如果这个sock处于监听状态,被动打开时的处理,包括收到SYN或ACK */  
    39.     if (sk->sk_state == TCP_LISTEN) {  
    40.         /* 返回值: 
    41.          * NULL,错误 
    42.          * nsk == sk,接收到SYN 
    43.          * nsk != sk,接收到ACK 
    44.          */  
    45.         struct sock *nsk = tcp_v4_hnd_req(sk, skb); /* 接收ACK的处理 */  
    46.   
    47.         if (! nsk)  
    48.             goto discard;  
    49.   
    50.         if (nsk != sk) { /* 接收到ACK时 */  
    51.             sock_rps_save_rxhash(nsk, skb);  
    52.   
    53.             if (tcp_child_process(sk, nsk, skb)) { /* 处理新的sock */  
    54.                 rsk = nsk;  
    55.                 goto reset;  
    56.             }  
    57.             return 0;  
    58.         }  
    59.     } else  
    60.         sock_rps_save_rx(sk, skb);  
    61.   
    62.     /* 处理除了ESTABLISHED和TIME_WAIT之外的所有状态 */  
    63.     if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {  
    64.         rsk = sk;  
    65.         goto reset;  
    66.     }  
    67.     return 0;  
    68.   
    69. reset:  
    70.     tcp_v4_send_reset(rsk, skb); /* 发送RST包 */  
    71.   
    72. discard:  
    73.     kfree_skb(skb);  
    74.     return 0;  
    75.   
    76. csum_err:  
    77.     TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);  
    78.     goto discard;  
    79. }  

    当收到客户端发送的SYN包时,会进入tcp_rcv_state_process()进行处理。

    1. /* 
    2.  * This function implements the receiving procedure of RFC 793 for all states except 
    3.  * ESTABLISHED and TIME_WAIT. 
    4.  * It's called from both tcp_v4_rcv and tcp_v6_rcv and should be address independent. 
    5.  */  
    6.   
    7. int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th,   
    8.               unsigned int len)  
    9. {  
    10.     struct tcp_sock *tp = tcp_sk(sk);  
    11.     struct inet_connection_sock *icsk = inet_csk(sk);  
    12.     int queued = 0;  
    13.   
    14.     tp->rx_opt.saw_tstamp = 0;  
    15.   
    16.     switch(sk->sk_state) {  
    17.         case TCP_CLOSE:  
    18.             goto discard;  
    19.   
    20.         case TCP_LISTEN:  
    21.             /* 收到SYN会走到这边,而ACK不会。 
    22.              * 所以直接向服务器发送ACK包,会收到RST包(使用SYN Cookie时除外)。 
    23.              */  
    24.             if (th->ack)   
    25.                 return 1;  
    26.   
    27.             if (th->rst)  
    28.                 goto discard;  
    29.   
    30.             if (th->syn) {  
    31.                 if (th->fin)  
    32.                     goto discard;  
    33.   
    34.                 /* 对于IPv4,对应的是ipv4_specific,调用tcp_v4_conn_request()处理收到的SYN包 */  
    35.                 if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)  
    36.                     return 1;  
    37.   
    38.                 /* Now we have several options: In theory there is nothing else in the frame. 
    39.                  * KA9Q has an option to send data with the syn, BSD accepts data with the syn up to 
    40.                  * the [to be] advertised window and Solaris 2.1 gives you a protocol error. For now 
    41.                  * we just ignore it, that fits the spec precisely and avoids incompatibilities. It would 
    42.                  * be nice in future to drop through and process the data. 
    43.                  * 
    44.                  * Now that TTCP is starting to be used we ought to queue this data. 
    45.                  * But, this leaves one open to an easy denial of service attack, and SYN cookies can't 
    46.                  * defend against this problem. So, we drop the data in the interest of security over 
    47.                  * speed unless it's still in use. 
    48.                  */  
    49.                  /* 这里讨论了SYN包携带数据的问题 */  
    50.   
    51.                  kfree_skb(skb);  
    52.                  return 0;  
    53.             }  
    54.   
    55.             goto discard;  
    56.         ...  
    57.     }  
    58.     ...  
    59. discard:  
    60.         __kfree_skb(skb);  
    61.     }  
    62.     return 0;  
    63. }  

    处理SYN包

    SYN包的处理是地址族相关的,我们要研究的是IPv4。

    1. /* 
    2.  * Pointers to address related TCP functions 
    3.  * (i.e. things that depend on the address family) 
    4.  */  
    5. struct inet_connection_sock_af_ops {  
    6.     ...  
    7.     int (*conn_request) (struct sock *sk, struct sk_buff *skb);  
    8.     ...  
    9. };  
    10.   
    11. const struct inet_connection_sock_af_ops ipv4_specific = {  
    12.     ...  
    13.     .conn_request = tcp_v4_conn_request, /* IPv4 SYN包的处理函数 */  
    14.     ...  
    15. };  

    服务器端处理接收到的SYN包。

    1. int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)  
    2. {  
    3.     struct tcp_extend_values tmp_ext;  
    4.     struct tcp_options_received tmp_opt;  
    5.     const u8 *hash_location;  
    6.     struct request_sock *req;  
    7.     struct inet_request_sock *ireq;  
    8.     struct tcp_sock *tp = tcp_sk(sk);  
    9.     struct dst_entry *dst = NULL;  
    10.     __be32 saddr = ip_hdr(skb)->saddr;  
    11.     __be32 daddr = ip_hdr(skb)->daddr;  
    12.     __u32 isn = TCP_SKB_CB(skb)->when;  
    13.     bool want_cookie = false;  
    14.   
    15.     /* Never answer to SYNs send to broadcast or multicast. 
    16.      * 忽略广播、多播的SYN段。 
    17.      */  
    18.     if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))  
    19.         goto drop;  
    20.   
    21.     /* 如果半连接队列满了 
    22.      * when变量在tcp_v4_rcv()中置0。 
    23.      */  
    24.     if (inet_csk_reqsk_queue_is_full(sk) && ! isn) {  
    25.   
    26.        /* 判断是直接丢弃,还是使用SYN Cookie */  
    27.         want_cookie = tcp_syn_flood_action(sk, skb, "TCP");  
    28.   
    29.         if (! want_cookie)  
    30.             goto drop; /* 如果不允许使用SYN Cookie,则直接丢弃 */  
    31.     }  
    32.   
    33.     /* Accept backlog is full. If we have already queued enough of warm entries in 
    34.      * syn queue, drop request. It is better than clogging syn queue with openreqs with 
    35.      * exponentially increasing timeout. 
    36.      */  
    37.     /* 如果全连接队列满了,且有未重传过的半连接,则直接丢弃SYN请求 */  
    38.     if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)  
    39.         goto drop;  
    40.   
    41.     /* 从缓存块中分配一个request_sock实例,指定此实例的操作函数集为tcp_request_sock_ops */  
    42.     req = inet_reqsk_alloc(&tcp_request_sock_ops);   
    43.     if (! req)  
    44.         goto drop;  
    45.   
    46. #ifdef CONFIG_TCP_MD5SIG  
    47.     tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops;  
    48. #endif  
    49.   
    50.     tcp_clear_options(&tmp_opt); /* 清零TCP选项 */  
    51.     tmp_opt.mss_clamp = TCP_MSS_DEFAULT; /* 默认的MSS为536 */  
    52.     tmp_opt.user_mss = tp->rx_opt.user_mss; /* mss requested by user in ioctl */  
    53.     tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); /* 全面解析TCP选项,并保存 */  
    54.   
    55.      /* 注意这部分实现的是:TCP Cookie Transaction (TCPCT) 选项。 
    56.       * TCPCT选项在2013年3月从内核代码中移除了! 
    57.       * 这个选项是在2009年加入的,功能类似于SYN Cookie。 
    58.       */  
    59.     if (tmp_opt.cookie_plus > 0 && tmp_opt.saw_tstamp && ! tp->rx_opt.cookie_out_never &&  
    60.          (sysctl_tcp_cookie_size > 0 || (tp->cookie_values != NULL &&  
    61.            tp->cookie_values->cookie_desired > 0))) {  
    62.         u8 *c;  
    63.         u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];  
    64.         int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; /* Cookie长度 */  
    65.   
    66.         if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)  
    67.             goto drop_and_release;  
    68.   
    69.         /* Secret recipe starts with IP addresses */  
    70.         *mess++ ^= (__force u32) daddr;  
    71.         *mess++ ^= (__force u32) saddr;  
    72.   
    73.         /* plus variable length Initiator Cookie */  
    74.         c = (u8 *) mess;  
    75.         while (l-- > 0)  
    76.             *c++ ^= *hash_location++;  
    77.   
    78.         want_cookie = false/* not our kind of cookie */  
    79.         tmp_ext.cookie_out_never = 0/* false */  
    80.         tmp_ext.cookie_plus = tmp_opt.cookie_plus;  
    81.     } else if (! tp->rx_opt.cookie_in_always) {  
    82.         /* redundant indications, but ensure initialization. */  
    83.         tmp_ext.cookie_out_never = 1/* true */  
    84.         tmp_ext.cookie_plus = 0;  
    85.     } else {  
    86.         goto drop_and_release;  
    87.     }  
    88.   
    89.     tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;  
    90.     /* Code above have already been removed in mainstream. */      
    91.   
    92.     /* 如果启用了SYN Cookie,且连接不使用TIMESTAMP选项 */  
    93.     if (want_cookie && ! tmp_opt.saw_tstamp)  
    94.         tcp_clear_options(&tmp_opt); /* 清零TCP选项 */  
    95.   
    96.     tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;  
    97.   
    98.     /* 初始化连接请求块,保存连接信息 */  
    99.     tcp_openreq_init(req, &tmp_opt, skb);  
    100.     ireq->loc_addr = daddr; /* 本端IP地址 */  
    101.     ireq->rmt_addr = saddr; /* 对端IP地址 */  
    102.     ireq->no_srccheck = inet_sk(sk)->transparent;  
    103.     ireq->opt = tcp_v4_save_options(sk, skb); /* 保存IP选项 */  
    104.   
    105.     if (security_inet_conn_request(sk, skb, req)) /* SELinux相关 */  
    106.         goto drop_and_free;  
    107.    
    108.     /* 如果没使用SYN Cookie,或者使用了TIMESTAMP选项 */  
    109.     if (! want_cookie || tmp_opt.tstamp_ok)  
    110.         TCP_ECN_create_request(req, skb); /* 判断连接是否要启用ECN */  
    111.   
    112.     if (want_cookie) { /* 如果使用SYN Cookie */  
    113.         isn = cookie_v4_init_sequence(sk, skb, &req->mss); /* 计算Cookie的值 */  
    114.         req->cookie_ts = tmp_opt.tstamp_ok;  
    115.   
    116.     } else if (! isn) {  
    117.         struct flowi4 fl4;  
    118.   
    119.         /* VJ's idea. We save last timestamp seen from destination in peer table, 
    120.          * when entering state TIME-WAIT, and check against it before accepting new 
    121.          * connection request. 
    122.          * If isn is not zero, this request hit alive timewait bucket, so that all the necessary 
    123.          * checks are made in the function processing timewait state. 
    124.          */  
    125.         /* TIME-WAIT状态检查,要确定是否PAWS */  
    126.         if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle &&  
    127.             (dst = inet_csk_route_req(sk, &fl4, req)) != NULL && fl4.daddr = saddr) {  
    128.             if (! tcp_peer_is_proven(req, dst, true)) {  
    129.                 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);  
    130.                 goto drop_and_release;  
    131.             }  
    132.         } else if (! sysctl_tcp_syncookies &&   
    133.              (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2))  
    134.                 && ! tcp_peer_is_proven(req, dst, false)) {  
    135.             /* Without syncookies last quarter of backlog is filled with destinations, proven to be alive. 
    136.              * It means that we continue to communicate to destinations, already remembered to the 
    137.              * moment of synflood. 
    138.              */  
    139.             LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u "), &saddr,  
    140.                           ntohs(tcp_hdr(skb)->source));  
    141.         }  
    142.   
    143.         isn = tcp_v4_init_sequence(skb); /* 本端的初始序列号 */  
    144.     }  
    145.   
    146.     tcp_rsk(req)->snt_isn = isn; /* 保存本端的初始序列号 */  
    147.     tcp_rsk(req)->snt_synack = tcp_time_stamp; /* 记录SYNACK的发送时间 */  
    148.   
    149.     /* 发送SYNACK包,如果使用SYN Cookie则不把这个req链接到半连接队列中 */  
    150.     if (tcp_v4_send_synack(sk, dst, req, (struct request_values *)&tmp_ext,   
    151.               skb_get_queue_mapping(skb), want_cookie) || want_cookie)  
    152.         goto drop_and_free;  
    153.   
    154.     /* 把连接请求块链入半连接队列,设置超时时间,启动定时器 */  
    155.     inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);  
    156.     return 0;  
    157.   
    158. drop_and_release:  
    159.     dst_release(dst);  
    160.   
    161. drop_and_free:  
    162.     reqsk_free(req);  
    163.   
    164. drop:  
    165.     return 0;  
    166. }  

    队列长度

    判断半连接队列是否满了。

    1. static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)  
    2. {  
    3.     return reqsk_queue_is_full(&inet_csk(sk)->icsk_accept_queue);  
    4. }  

    半连接队列的最大长度为:2^max_qlen_log。

    1. static inline int reqsk_queue_is_full(const struct request_sock_queue *queue)  
    2. {  
    3.     return queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;  
    4. }  

    判断全连接队列是否满了,全连接队列的最大长度为:sk->sk_max_ack_backlog。

    1. static inline bool sk_acceptq_is_full(const struct sock *sk)  
    2. {  
    3.     return sk->sk_ack_backlog > sk->sk_max_ack_backlog;  
    4. }  

    获取未重传过SYNACK的半连接个数。

    1. static inline int inet_csk_reqsk_queue_young(const struct sock *sk)  
    2. {  
    3.     return reqsk_queue_len_young(&inet_csk(sk)->icsk_accept_queue);  
    4. }  
    5.   
    6. static inline int reqsk_queue_len_young(const struct requst_sock_queue *queue)  
    7. {  
    8.     return queue->listen_opt->qlen_young;  
    9. }   

    初始序列号

    根据源IP、目的IP、源端口、目的端口计算出本端的初始序列号isn。

    1. static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb)  
    2. {  
    3.     return secure_tcp_sequence_number(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr,   
    4.                             tcp_hdr(skb)->dest, tcp_hdr(skb)->source);  
    5. }  
    6.   
    7. __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport)  
    8. {  
    9.     u32 hash[MD5_DIGEST_WORDS];  
    10.   
    11.     hash[0] = (__force u32) saddr;  
    12.     hash[1] = (__force u32) daddr;  
    13.     hash[2] = ((__force u16) sport << 16) + (__force u16) dport;  
    14.     hash[3] = net_secret[15]; /* 获取一个随机数 */  
    15.   
    16.     md5_transform(hash, net_secret); /* 计算MD5值 */  
    17.    
    18.     return seq_scale(hash[0]);  
    19. }  
    1. #define MD5_DIGEST_WORDS 4  
    2. #define MD5_MESSAGE_BYTES 64  
    3.   
    4. static u32 net_secret[MD5_MESSAGE_BYTES / 4] __cacheline_aligned;  
    5.   
    6. static int __init net_secret_init(void)  
    7. {  
    8.     get_random_bytes(net_secret, sizeof(net_secret)); /* 随机获取 */  
    9.     return 0;  
    10. }  
    11.   
    12. /* 
    13.  * This function is the exported kernel interface. 
    14.  * It returns some number of good random numbers, suitable for key generation, 
    15.  * seeding TCP sequence numbers, etc. It does not use the hw random number 
    16.  * generator, if available; use get_random_bytes_arch() for that. 
    17.  */  
    18. void get_random_bytes(void *buf, int bytes) {};  
    19.   
    20. static u32 seq_scale(u32 seq)  
    21. {  
    22.     return seq + (ktime_to_ns(ktime_get_real()) >> 6);  
    23. }  

    最终使用MD5。

    Message Digest Algorithm 5,消息摘要算法第五版。是一种散列函数,用于提供消息的完整性保护。

    除了MD5外,比较著名的还有SHA1。

    void md5_transform(__u32 *hash, __u32 const *in) {}

  • 相关阅读:
    14、Cahin of Responsibility 责任链 COR设计模式
    13、Visitor 访问者模式 访问数据结构并处理数据 行为型设计模式
    HTML inline 与block元素
    javascript 继承实现
    深入理解linux的权限设置和SUID,SGID以及粘滞位
    设置express ejs模板的后缀名html
    Centos 安装编译codeblocks&&codelite
    JavaScript中两种类型的全局对象/函数【转】
    Canvas vs. SVG[转]
    centos 学习总结
  • 原文地址:https://www.cnblogs.com/ztguang/p/12644867.html
Copyright © 2020-2023  润新知