Chinaunix首页 | 论坛 | 博客
  • 博客访问: 757925
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1150
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-17 14:32
个人简介

小公司研发总监,既当司令也当兵!

文章分类

全部博文(144)

分类: LINUX

2015-09-16 14:27:29

MTUIP MTUTCP MSS说明

1.     概述

本文主要分析了二层MTUIP MTUMSS之间的关系和在不同网络场景中的应用,最后通过一个案例分析来进一步认识MTU对实际IP数据包转发的影响。

2.     MTU

最大传输单元(Maximum Transmission UnitMTU)是指一种通信协议在某一层上面所能通过的最大数据报大小(以字节为单位),它通常与链路层协议有密切的关系。EthernetII帧结构如下:

 

DMAC

SMAC

Type

Data

CRC

 

由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64bytes,最大不能超过1518bytes,对于小于或者大于这个限制的以太网帧,我们都可以视之为错误的数据帧。一般的以太网转发设备会丢弃这些数据帧。(注:小于64Bytes的数据帧一般是由于以太网冲突产生的 碎片或者线路干扰或者坏的以太网接口产生的,对于大于1518Bytes的数据帧我们一般把它叫做Giant帧,这种一般是由于线路干扰或者坏的以太网口产生)。

由于以太网EthernetII最大的数据帧是1518Bytes,除去以太网帧的帧头(DMAC目的MAC地址 48bit=6Bytes+SMACMAC地址48bit=6Bytes+Type2bytes14Bytes和帧尾CRC校验部分4Bytes (这个部份有时候大家也把它叫做FCS),那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes,这个值我们就把它称之为MTU

这个MTU就是网络层协议非常关心的地方,因为网络层协议比如IP协议会根据这个值来决定是否把上层传下来的数据进行分片。就好比一个盒子没法装下一大块面包,我们需要把面包切成片,装在多个盒子里面一样的道理。当两台远程PC互联的时候,它们的数据需要穿过很多的路由器和各种各样的网络媒介才能到达对端,网络中不同媒介的MTU各不相同,就好比一长段的水管,由不同粗细的水管组成(MTU不同 )通过这段水管最大水量就要由中间最细的水管决定。

3.     IP MTU

对于网络层的上层协议而言(我们以TCP/IP协议族为例),网络层IP协议会检查每个从上层协议下来的数据包的大小,并根据本机MTU的大小决定是否作分片处理。分片最大的坏处就是降低了传输性能,本来一次可以搞定的事情,分成多次搞定,所以在网络层更高一层(就是传输层)的实现中往往会对此加以注意!有些高层因为某些原因就会要求我这个面包不能切片,我要完整地面包,所以会在IP数据包包头里面加上一个标签:DFDonot Fragment)。这样当这个IP数据包在一大段网络(水管里面)传输的时候,如果遇到MTU小于IP数据包的情况,转发设备就会根据要求丢弃这个数据包,然后返回一个错误信息给发送者。这样往往会造成某些通讯上的问题,不过幸运的是大部分网络链路MTU都是等于1500或者大于1500

对于UDP协议而言,这个协议本身是无连接的协议,对数据包的到达顺序以及是否正确到达不甚关心,所以一般UDP应用对分片没有特殊要求。对于TCP协议而言就不一样了,这个协议是面向连接的协议,对于TCP协议而言它非常在意数据包的到达顺序以及是否传输中有错误发生。所以有些TCP应用对分片有要求---不能分片(DF)。

4.     MSS

MSS是最大传输大小的缩写,它是TCP协议里面的一个概念。如下图1-1所示:

关于MTU、IP MTU、TCP MSS探讨与分析 - 狂人_club - __殘 劍
 

1-1 TCP头部

注:URG等参数指的是 ACK URG PSH SIN FIN RST等参数

TCP报文中 MSS的位置就在选项的位置,根据RFC1323RFC793规定,选项中内容有很多种,MSS是其中的一种,用kind=2表示;kind=1表示无操作,kind=4567称为选择ACK及回显选项,但是由于回显选项已经被时间戳选项取代,同时,目前定义的选择ACK选项仍未定论,也没有包括在RFC1323中,所以具体代表什么含义还无定论。在实际网络数据传输,要求MSS+20TCP包头 +20 IP包头不大于MTUMSSTCP报文中是可选项,不是必选项,换句话说,MSS是可协商项,而且在协商过后,该选项内容可以改变,也可以没有;在协商MSS时,一般是建立TCP连结的两端发送Syn标志报文时互相通报,然后选取最小MSS作为双方的约定,如果双方都不通报或有一方不通报。

MSS就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能,TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的时候往往用MTU值代替(需要减去IP数据包包头的大小20BytesTCP数据段的包头20Bytes),所以往往MSS1460。通讯双方会根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。

5. 区别及联系

由前面的叙述可知:MTU是一个二层的概念,以太网最大的mtu就是1500(它是不包含二层头部的,加上头部应该为1518 bytes),当然这里说的是很常规的情况,也有些server,比如server 2008,出来的就是jumbo frame了,我们在这里讨论常规情况。IP MTU是一个三层概念,它包含了三层头部及所有载荷,根据下层为上层服务的,上层基于下层才能做进一步的扩展的原则,尽管IP MTU的变化范围很大(68-65535),但也不得不照顾以太网MTU的限制,说白了就是ip对以太网的妥协。MSSTCP里面的一个概念,它是TCP数据包每次能够传输的最大数据分段,不包含包头部分,它与IP MTU满足如下关系:IP MTU=MSS+20bytesIP包头)+20bytesTCP包头)。当然,如果传输的时候还承载有其他协议,还要加些包头在前面,简言之,mtu就是总的最后发出去的报文大小,MSS就是需要发出去的数据大小,比如PPPoE,就是在以太网上承载PPP协议(点到点连接协议),它包括6bytesPPPoE头部和2bytesPPP协议ID号,此时,由于以太网的MTU值为1500,所以上层PPP负载数据不能超过1492字节,也就是相当于在PPPOE环境下的MTU1492字节,MSS1452字节。

6.网络中MSS不匹配引起访问失败的问题实例分析
问题描述:
    在某个路由项目中,出现在深圳地区,部分电信pppoe拨号网络,出现通过路由器拨号上网,部分网站(土豆、腾讯视频、米酷音乐等)无法访问。
分析过程:
    (1)通过检查路由、防火墙、DNS配置,发现网络连接是正常的;
    (2)通过抓包分析,发现连接这些网站是正常的(TCP 三次握手成功,连接建立,http请求正常发送,并且收到ACK),但就是没有收到服务端回复的数据包;
    (3)通过分析抓包文件,发现TCP连接建立时,MSS协商为1460字节(以太网默认值);在PPPOE环境中,MSS最大应该是1452字节(原因见前文),因此锁定问题出在MSS上。
问题原因:
    由于PPPOE拨号客户端没有对MSS进行适配,而采用默认的1460字节,在TCP连接建立后,部分网站按1460字节的有效负载进行数据传送,并且(可能)设置了不允许分片标志(这样可以使网络性能优化,对于音视频播放效果更流畅);PPPOE服务端接收到该报文后,由于无法添加PPP头部和PPPOE头部信息(1460+20tcp头+20ip头 已经达到MTU限制),并且该报文不允许分片,只得丢弃该报文,因而客户端无法收到数据报文。
修复方案:
    在pppoe数据转发过程中,检查TCP连接状态,拦截SYN和RESET的报文,对其中MSS选项进行解析,如果发现MSS选项描述值比当前网络设定值(1412字节 = MTU(1500B) - PPPOE头部(6B)- PPP头(2B) - IP头部(20B)- TCP头部(20B) - TCP选项(40B))大,则修改为当前网络设定值。经测试,修改MSS后上述网站访问均正常。

7. 附 MSS修改代码
    

点击(此处)折叠或打开

  1. /**********************************************************************
  2. * FUNCTION_NAME: clamp_MSS
  3. * DESCRIPTION: Check and clamps MSS if TCP SYN flag is set.
  4. * INPUT:
  5. * OUTPUT:
  6. * RETURN:
  7. * AUTHOR: khls 
  8. ***********************************************************************/
  9. static void clamp_MSS(struct sk_buff *skb, int clampmss)
  10. {
  11.     unsigned char *tcphdr;
  12.     unsigned char *iphdr;
  13.     unsigned char *opt;
  14.     unsigned char *endhdr;
  15.     unsigned char *mssopt = NULL;
  16.     UINT16_t csum;
  17.     int len, minlen;
  18.  
  19.     iphdr = skb->data;
  20.     minlen = 40;
  21.     
  22.     /* Is it too short? */
  23.     len = (int) ntohs(skb->len);
  24.     if (len < minlen)
  25.     {
  26.         /* 20 byte IP header; 20 byte TCP header */
  27.         return;
  28.     }

  29.     /* Verify once more that it's IPv4 */
  30.     if ((iphdr[0] & 0xF0) != 0x40)
  31.     {
  32.         return;
  33.     }

  34.     /* Is it a fragment that's not at the beginning of the packet? */
  35.     if ((iphdr[6] & 0x1F) || iphdr[7]) {
  36.         /* Yup, don't */
  37.         return;
  38.     }
  39.     
  40.     /* Is it TCP? */
  41.     if (iphdr[9] != 0x06) {
  42.         return;
  43.     }

  44.     /* Get start of TCP header */
  45.     tcphdr = iphdr + (iphdr[0] & 0x0F) * 4;

  46.     /* Is SYN set? */
  47.     if (!(tcphdr[13] & 0x02)) {
  48.         return;
  49.     }

  50.     /* Compute and verify TCP checksum -- do not touch a packet with a bad checksum */
  51.     csum = compute_TCP_checksum(iphdr, tcphdr);
  52.     if (csum)
  53.     {
  54.         printk(KERN_INFO, "Bad TCP checksum %x", (unsigned int) csum);
  55.         /* Upper layers will drop it */
  56.         return;
  57.     }

  58.     /* Look for existing MSS option */
  59.     endhdr = tcphdr + ((tcphdr[12] & 0xF0) >> 2);
  60.     opt = tcphdr + 20;
  61.     while (opt < endhdr)
  62.     {
  63.         if (!*opt)
  64.             break;    /* End of options */
  65.         
  66.         switch(*opt)
  67.         {
  68.         case 1:
  69.          opt++;
  70.             break;

  71.         case 2:
  72.          if (opt[1] != 4)
  73.             {
  74.                 /* Something fishy about MSS option length. */
  75.                 printk(KERN_INFO "Bogus length for MSS option (%u) from %u.%u.%u.%u\n",
  76.                   (unsigned int) opt[1],
  77.                   (unsigned int) iphdr[12],
  78.                   (unsigned int) iphdr[13],
  79.                   (unsigned int) iphdr[14],
  80.                  (unsigned int) iphdr[15]);
  81.                 return;
  82.          }
  83.               mssopt = opt;
  84.             break;
  85.         default:
  86.          if (opt[1] < 2)
  87.             {
  88.                 /* Someone's trying to attack us? */
  89.                 printk(KERN_INFO "Bogus TCP option length (%u) from %u.%u.%u.%u\n",
  90.                   (unsigned int) opt[1],
  91.                  (unsigned int) iphdr[12],
  92.                  (unsigned int) iphdr[13],
  93.                  (unsigned int) iphdr[14],
  94.                  (unsigned int) iphdr[15]);
  95.                 return;
  96.          }
  97.          opt += (opt[1]);
  98.          break;
  99.         }
  100.         
  101.         /* Found existing MSS option? */
  102.         if (mssopt)
  103.             break;
  104.     }

  105.     /* If MSS exists and it's low enough, do nothing */
  106.     if (mssopt)
  107.     {
  108.         unsigned int mss = mssopt[2] * 256 + mssopt[3];
  109.         if (mss <= clampmss)
  110.         {
  111.          return;
  112.         }

  113.         mssopt[2] = (((unsigned) clampmss) >> 8) & 0xFF;
  114.         mssopt[3] = ((unsigned) clampmss) & 0xFF;
  115.     }
  116.     else
  117.     {
  118.         /* No MSS option. Don't add one; we'll have to use 536. */
  119.         return;
  120.     }

  121.     /* Recompute TCP checksum */
  122.     tcphdr[16] = 0;
  123.     tcphdr[17] = 0;
  124.     csum = compute_TCP_checksum(iphdr, tcphdr);
  125.     (* (UINT16_t *) (tcphdr+16)) = csum;
  126. }



阅读(2961) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~