加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

linux 内核对于TCPMSS的处理

(2012-08-20 11:29:15)
标签:

network

it

分类: Kernel
1 概念
  先介绍几个概念:
   1) MTU: 网卡的最大传输单元
   2) MSS: Maxitum Segment Size 最大分段大小
2  MSS 作用
   MSS表示TCP数据包的每次能够传输的最大数据分段。MSS的主要作用是在TCP建立连接的过程通常要写上对发的MSS值,这个值是TCP协议实现的时候根据MTU换算而得(主要是1500-20个大小的包头-20个大小的TCP数据包头)。因此一般的MSS值大小为1460. 
3 协商过程
    TCP client 发送SYN包,在Option的选项里面有MSS字段。
    同样在server端收到syn数据包后,会相应SYN,ACK包,其中OPTION字段里面包含MSS字段。
    在此过程中双方会比较本地的MSS和对方的MSS,得到最小的MSS为此连接的MSS值。
4 应用
    那么这个MSS值到底有什么作用呢。在我们发送的时候,send函数,后面没有要求一次放松多少个字节,这是因为协议栈自动的为我们进行了分段处理。比如一次放松1W个字节,协议栈会根据MSS值来决定一次TCP的发送。这样在IP层就不用再进行分片或者其他的处理。
    在以linux 内核协议栈进行软处理的路由器中,如果不对MSS进行特殊处理的话,如果转发的包大小大于路由器的MTU的时候,路由器往往会根据本地的MTU进行分片处理。
    当设计到PPPOE,L2TP,GRE等组网的条件时,报文分片会降低传输速率。
    如果组网的环境MTU值本身比较小,而路由器不能动态察觉的时候,从server返回的大包由于大小大于中间设备的MTU值,造成中间过程被丢弃掉。在我们常使用的应用如: email 等其他的https连接的服务,通过server端返回的包大都是根据MSS值的往往比较大。可以通过协议栈netfilter里面的TCP MSS来进行解决。
5 路由器软协议栈TCP MSS的的解决

iptables -A FORWARD -p tcp- -tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

这条规则的目的就是改变TCP MSS以适应PMTU(Path MTU)

 

iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN- j TCPMSS --set-mss 1400

设置MSS为1400


6 内核对于TCP MSS的处理

   那么内核对于TCP MSS的处理到底在哪边呢? 主要是在net/netfilter/xt_TCPMSS.c里面:

 

static int

tcpmss_mangle_packet(struct sk_buff **pskb,

                     const struct xt_tcpmss_info *info,

                     unsigned int tcphoff,

                     unsigned int minlen)

{

        struct tcphdr *tcph;

        unsigned int tcplen, i;

        __be16 oldval;

        u16 newmss;

        u8 *opt;


        if (!skb_make_writable(pskb, (*pskb)->len))

                return -1;


        tcplen = (*pskb)->len - tcphoff;

        tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff);


       

        if (tcplen != tcph->doff*4) {

                if (net_ratelimit())

                        printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n",

                               (*pskb)->len);

                return -1;

        }


        if (info->mss == XT_TCPMSS_CLAMP_PMTU) {

                if (dst_mtu((*pskb)->dst) <= minlen) {

                        if (net_ratelimit())

                                printk(KERN_ERR "xt_TCPMSS: "

                                       "unknown or invalid path-MTU (%u)\n",

                                       dst_mtu((*pskb)->dst));

                        return -1;

                }

根据目的网卡的MTU进行newMSS的设置

                newmss = dst_mtu((*pskb)->dst) - minlen;

        } else

                newmss = info->mss;


        opt = (u_int8_t *)tcph;

        for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {

                if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&

                    opt[i+1] == TCPOLEN_MSS) {

                        u_int16_t oldmss;

 

 oldmss = (opt[i+2] << 8) | opt[i+3];


                        if (info->mss == XT_TCPMSS_CLAMP_PMTU &&

                            oldmss <= newmss)

                                return 0;


                        opt[i+2] = (newmss & 0xff00) >> 8;

                        opt[i+3] = (newmss & 0x00ff);


                        nf_proto_csum_replace2(&tcph->check, *pskb,

                                               htons(oldmss), htons(newmss), 0);

                        return 0;

                }

        }


       

        if (skb_tailroom((*pskb)) < TCPOLEN_MSS) {

                struct sk_buff *newskb;


                newskb = skb_copy_expand(*pskb, skb_headroom(*pskb),

                                         TCPOLEN_MSS, GFP_ATOMIC);

                if (!newskb)

                        return -1;

                kfree_skb(*pskb);

                *pskb = newskb;

                tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff);

        }


        skb_put((*pskb), TCPOLEN_MSS);


        opt = (u_int8_t *)tcph + sizeof(struct tcphdr);

        memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));


        nf_proto_csum_replace2(&tcph->check, *pskb,

                               htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);

        opt[0] = TCPOPT_MSS;

        opt[1] = TCPOLEN_MSS;

设置新的MSS值

        opt[2] = (newmss & 0xff00) >> 8;

        opt[3] = (newmss & 0x00ff);


        nf_proto_csum_replace4(&tcph->check, *pskb, 0, *((__be32 *)opt), 0);


        oldval = ((__be16 *)tcph)[6];

        tcph->doff += TCPOLEN_MSS/4;

 

 nf_proto_csum_replace2(&tcph->check, *pskb,

                                oldval, ((__be16 *)tcph)[6], 0);

        return TCPOLEN_MSS;

}





 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有