zoukankan      html  css  js  c++  java
  • (1) linux 3.x


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


    本文主要分析:在收到客户端的SYN包时,服务器端是如何解析它所携带的TCP选项,并结合本端情况决定是否予以支持。

    内核版本:3.6

    Author:zhangskd @ csdn blog

    概述

    收到客户端的SYN包时,需要全面的解析它携带的TCP选项,这样我们就知道客户端支持哪些选项,如果本端也支持,

    那么连接就支持这些TCP选项。这些信息在连接建立的过程中,是保存在连接请求块的(request_sock、inet_request_sock、

    tcp_request_sock)。

    函数调用路径:

    tcp_v4_conn_request

            |--> tcp_parse_options

    3.6版本Linux内核支持的TCP选项包括:

    NOP,用于填充。

    Max Segment Size,最大分段大小。

    Window Scaling,窗口扩大因子。

    SACK Permit,是否允许使用SACK。

    SACK,携带SACK块。

    Timestamp,时间戳选项。

    MD5SIG,MD5签名。

    Cookie,Cookie extension,2013年3月已从内核移除。

    EXP,Experimental,处于实验阶段的选项,新版本用于Fast Open。

    实现

    全面解析数据包的TCP选项,并保存到指定的tcp_options_received实例中。

    1. /* Look for tcp options. Normally only called on SYN and SYNACK packets. 
    2.  * But, this can also be called on packets in the established flow when the fast version below fails. 
    3.  */  
    4.   
    5. void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx, const u8 **hvpp,  
    6.                        int estab, struct tcp_fastopen_cookie *foc)  
    7. {  
    8.     const unsigned char *ptr;  
    9.     const struct tcphdr *th = tcp_hdr(skb);  
    10.     int length = (th->doff * 4) - sizeof(struct tcphdr); /* 选项的总长度 */  
    11.   
    12.     ptr = (const unsigned char *) (th + 1); /* 选项的起始地址 */  
    13.     opt_rx->saw_tstamp = 0/* Saw TIMESTAMP on last packet */  
    14.   
    15.     while(length > 0) {  
    16.         int opcode = *ptr++; /* 选项kind */  
    17.         int opsize; /* 选项length */  
    18.   
    19.         switch(opcode) {  
    20.         case TCPOPT_EOL: /* End of options */  
    21.             return;   
    22.   
    23.         case TCPOPT_NOP: /* Padding,填充选项 */  
    24.             length--; /* 此选项只占一个字节 */  
    25.             continue;  
    26.   
    27.         default:  
    28.             opsize = *ptr++; /* 选项长度 */  
    29.             if (opsize < 2/* silly options */  
    30.                 return/* 选项长度过小 */  
    31.   
    32.             if (opsize > length)  
    33.                 return/* don't parse partial options */  
    34.   
    35.             switch(opcode) {  
    36.             case TCPOPT_MSS: /* MSS选项 */  
    37.                 if (opsize == TCPOLEN_MSS && th->syn && ! estab) {  
    38.                     u16 in_mss = get_unaligned_be16(ptr);  
    39.   
    40.                     if (in_mss) {  
    41.                         /* 如果用户指定了MSS,且比对端通告的小 */  
    42.                         if (opt_rx->user_mss && opt_rx->user_mss < in_mss)  
    43.                             in_mss = opt_rx->user_mss;  
    44.   
    45.                         opt_rx->mss_clamp = in_mss; /* Maximal mss */  
    46.                     }  
    47.                 }  
    48.                 break;  
    49.   
    50.             case TCPOPT_WINDOW: /* Window scaling */  
    51.                 if (opsize == TCPOLEN_WINDOW && th->syn && ! estab &&  
    52.                     sysctl_tcp_window_scaling) {  
    53.                     __u8 snd_wscale = *(__u8 *)ptr; /* 对端的接收窗口扩大因子 */  
    54.                     opt_rx->wscale_ok = 1/* 连接使用窗口扩大选项 */  
    55.   
    56.                     if (snd_wscale > 14) {  
    57.                         net_info_ratelimited("%s: Illegal window scaling value %d >14 received ",  
    58.                                __func__, snd_wscale);  
    59.                         snd_wscale = 14;  
    60.                     }  
    61.   
    62.                     opt_rx->snd_wscale = snd_wscale; /* 保存对端的接收窗口扩大因子 */  
    63.                 }  
    64.                 break;  
    65.   
    66.             case TCPOPT_TIMESTAMP: /* Better RTT estimations/PAWS */  
    67.                 if ((opsize == TCPOLEN_TIMESTAMP) && ((estab && opt_rx->tstamp_ok) ||  
    68.                      (!estab && sysctl_tcp_timestamps))) {  
    69.   
    70.                     opt_rx->saw_tstamp = 1/* 连接支持TIMESTAMP选项 */  
    71.                     opt_rx->rcv_tsval = get_unaligned_be32(ptr); /* Time stamp value */  
    72.                     opt_rx->rcv_tsecr = get_unaligned_be32(ptr + 4); /* Time stamp echo reply */  
    73.                 }  
    74.                 break;  
    75.   
    76.             case TCPOPT_SACK_PERM: /* SACK Permitted */  
    77.                 if (opsize == TCPOLEN_SACK_PERM && th->syn && !estab && sysctl_tcp_sack) {  
    78.                     opt_rx->sack_ok = TCP_SACK_SEEN; /* 连接支持SACK选项 */  
    79.                     tcp_sack_reset(opt_rx); /* 清空dsack和num_sacks变量 */  
    80.                 }  
    81.                 break;  
    82.   
    83.             case TCPOPT_SACK: /* SACK Block */  
    84.                 if ((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&  
    85.                     !((opsize - TCPOLEN_SACK_BASE) % TCPOLEN_SACK_PERBLOCK) && opt_rx->sack_ok) {  
    86.                     /* 保存SACK选项的起始偏移地址 */  
    87.                     TCP_SKB_CB(skb)->sacked = (ptr - 2) - (unsigned char *) th;   
    88.                 }  
    89.                 break;  
    90.   
    91. #ifdef CONFIG_TCP_MD5SIG  
    92.             case TCPOPT_MD5SIG:  
    93.                 /* The MD5 Hash has already been checked 
    94.                  * (see tcp_v{4, 6}_do_rcv()). 
    95.                  */  
    96.                 break;  
    97. #endif  
    98.   
    99.             /* 注意,在新版内核中此选项已被删除。Cookie extension (experimental) */  
    100.             case TCPOPT_COOKIE:   
    101.                 /* This option is variable length. */  
    102.                 switch(opsize) {  
    103.                 case TCPOLEN_COOKIE_BASE:  
    104.                     /* not yet implemented */  
    105.                     break;  
    106.   
    107.                 case TCPOLEN_COOKIE_PAIR:  
    108.                     /* not yet implemented */  
    109.                     break;  
    110.   
    111.                 case TCPOLEN_COOKIE_MIN + 0:  
    112.                 case TCPOLEN_COOKIE_MIN + 2:  
    113.                 case TCPOLEN_COOKIE_MIN + 4:  
    114.                 case TCPOLEN_COOKIE_MIN + 6:  
    115.                 case TCPOLEN_COOKIE_MAX:  
    116.   
    117.                     /* 16-bit multple */  
    118.                     opt_rx->cookie_plus = opsize;  
    119.                     *hvpp = ptr;  
    120.                     break;  
    121.   
    122.                 default:  
    123.                     /* ignore option */  
    124.                     break;  
    125.                 }  
    126.                 break;  
    127.    
    128.             case TCPOPT_EXP: /* Experimental */  
    129.                 /* Fast Open option shares code 254 using a 16 bits magic number. 
    130.                  * It's valid only in SYN or SYN-ACK with an even size. 
    131.                  */  
    132.                 if (opsize < TCPOLEN_EXP_FASTOPEN_BASE || get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC  
    133.                     || foc == NULL || ! th->syn || (opsize & 1))  
    134.                     break;  
    135.   
    136.                 foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE;  
    137.                 if (foc->len >= TCP_FASTOPEN_COOKIE_MIN && foc->len <= TCP_FASTOPEN_COOKIE_MAX)  
    138.                     memcpy(foc->val, ptr + 2, foc->len);  
    139.                 else if (foc->len != 0)  
    140.                     foc->len = -1;  
    141.                 break;  
    142.             }  
    143.   
    144.             ptr += opsize - 2;  
    145.             length -= opsize;  
    146.         }  
    147.     }  
    148. }  
    1. /* TCP options */  
    2. #define TCPOPT_NOP 1 /* Padding */  
    3. #define TCPOPT_EOL 0 /* End of options */  
    4. #define TCPOPT_MSS 2 /* Segment size negotiating */  
    5. #define TCPOPT_WINDOW 3 /* Window scaling */  
    6. #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */  
    7. #define TCPOPT_SACK_PERM 4 /* SACK Permitted */  
    8. #define TCPOPT_SACK 5 /* SACK Block */  
    9. #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */  
    10. #define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */  
    11. #define TCPOPT_EXP 254 /* Experimental */  
    12.   
    13. /* Magic number to be after the option value for sharing TCP experimental options. 
    14.  * See draft-ietf-tcpm-experimental-options-00.txt 
    15.  */  
    16. #define TCPOPT_FASTOPEN_MAGIC 0xF989  
    17.   
    18. /* TCP option lengths */  
    19. #define TCPOLEN_MSS 4  
    20. #define TCPOLEN_WINDOW 3  
    21. #define TCPOLEN_TIMESTAMP 10  
    22. #define TCPOLEN_SACK_PERM 2  
    23. #define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */  
    24. #define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */  
    25. #define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE + TCP_COOKIE_MIN)  
    26. #define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE + TCP_COOKIE_MAX)  
    27. #define TCPOLEN_EXP_FASTOPEN_BASE 4  
    28.   
    29. /* These are used to set the sack_ok field in struct tcp_options_received */  
    30. #define TCP_SACK_SEEN (1 << 0/* peer is SACK capable */  
    31. #define TCP_FACK_ENABLED (1 << 1/* FACK is enabled locally */  
    32. #define TCP_DSACK_SEEN (1 << 2/* DSACK was received from peer */  
    33.   
    34. /* for TCP_COOKIE_TRANSACTIONS (TCPCT) socket option */  
    35. #define TCP_COOKIE_MIN 8 /* 64-bits */  
    36. #define TCP_COOKIE_MAX 16 /* 128-bits */  
    37.   
    38. /* TCP Fast Open */  
    39. #define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */  
    40. #define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */  
    41.   
    42. /* TCP Fast Open Cookie as stored in memory */  
    43. struct tcp_fastopen_cookie {  
    44.     s8 len;  
    45.     u8 val[TCP_FASTOPEN_COOKIE_MAX];  
    46. };  
    47.   
    48. static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)  
    49. {  
    50.     rx_opt->dsack = 0;  
    51.     rx_opt->num_sacks = 0;  
    52. }  

    用于判断连接是否使用ECN。要注意的是在建立连接时,要求IP报的ECN域不能被设置,否则就禁用ECN。

    1. /* RFC3168: 6.1.1 SYN packets must not have ECT/ECN bits set. 
    2.  * 
    3.  * If we receive a SYN packet with these bits set, it means a network is playing  bad games 
    4.  * with TOS bits. In order to avoid possible false congestion notifications, we disable 
    5.  * TCP ECN negociation. 
    6.  */  
    7. static inline void TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb)  
    8. {  
    9.     const struct tcphdr *th = tcp_hdr(skb);  
    10.   
    11.     if (sysctl_tcp_ecn && th->ece && th->cwr && INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield))  
    12.         inet_rsk(req)->ecn_ok = 1/* 连接支持ECN */  
    13. }  

    清零TCP选项。

    1. static inline void tcp_clear_options(struct tcp_options_received *rx_opt)  
    2. {  
    3.     rx_opt->tstamp_ok = rx_opt->sack_ok = 0;  
    4.     rx_opt->wscale_ok = rx_opt->snd_wscale = 0;  
    5.     rx_opt->cookie_plus = 0;  
    6. }  

  • 相关阅读:
    keepalived安装
    Nginx学习笔记
    使用xhprof分析php性能
    使用 .bash_profile与.bashrc修改字符集
    Mysql分区简述
    c语言多线程队列读写
    setsockopt 设置 SO_LINGER 选项
    nginx配置rewrite
    使用PHP+ajax打造聊天室应用
    UDP/TCP通信小记
  • 原文地址:https://www.cnblogs.com/ztguang/p/12644862.html
Copyright © 2011-2022 走看看