世界远不是完美的!

造物主设计我们丰富又复杂的系统时,其考量仅仅是可用性,因此上帝远不是哲学家,而是类似于理工科偏工科方向硕士或者工程师那样的being。

食管和气管为何要有个交叉,这是一个重大BUG,类似这样的BUG还有很多。上帝既然都不是完美主义者,我们自己就更不是了,很多东西只要脱离了设计图纸,几乎就不会再被重新设计了,这就是事实,可能唯一的例外就是减数分裂带来的有性生殖了,所有的个体都是从一个受精卵开始有丝分裂,从减数分裂到整个成长的过程就是一个重新设计的过程,除此之外,再也没有什么是可以重新设计的了,你知道自己的产品有缺陷,但是你不会重新设计,你做的仅仅是小修小补罢了...

不合理的设计-Linux的tcp_tw_recycle

Linux中有一个tcp_tw_recycle参数,意在不保持tw状态的TCP连接,直接在一个重传间隔后释放。我相信这个机制绝不仅仅是为了节省内存,更大的意义在于可以实现tw状态的连接的快速重用,而不必非要等到tw状态过期,只要能保证不会发生由于不等待tw状态而发生的事故。

      悲剧就在于,tw状态的连接还真TMD回收了,完全被释放掉了,留下了一个仅由IP地址标识的peer,也就是说,在有时间戳的情况下,一个来自IP地址A的到端口P1的连接会影响到来自同样IP地址的到端口P2的连接。我们设想,有一个机器N开启了NAT,将所有数据包的源地址转换成了addr1,它后面有两台机器M1,M2,它前面连接一个服务器S开启端口P1,P2,这么简单个拓扑就能说明问题。M1和M2肯定有一台机器的时间戳偏低,假设是M2。
      现在假设M1连接S的P1,连接成功后,S主动断开到M1的连接,此时M2连接S的P2,悲剧发生了,S不再回复SYN/ACK,Why?因为由于NAT,显然,两次连接来自同一个peer,但是后来者的时间戳小于前面连接P1的时间戳,tw重用逻辑不认可后来者是一个正常的连接。可是,着明显是误判,虽然NAT确实会导致一系列的TW问题,但是目标端口明显是不同的,所有的问题最根本的就是保持五元组的唯一性,目的端口不同就已经保证了五元组不会冲突!
      如果仅仅为了TW快速重用,何必将TW状态的连接直接释放呢?何必不继续保持其数据结构,只是在冲突的新连接到来后判断时间戳或者初始序列号呢?等到tw到期后再释放,这样就可以精确比对协议元数据,最小化误判。可是这样的话,tcp_tw_recycle这个参数就失去了意义,万一短期(tw超时范围内)内没有冲突连接到来呢?我为何要保持一个根本不会用到的数据结构呢?快速回收意味着什么?仅仅意味着reuse么?引入一个仅由IP标识的peer是个多么不好的开端。因此,我冒犯地认为,tcp_tw_recycle这个参数根本就不该存在!

不合理的设计-OpenVPN的两类通道

OpenVPN创建了两个通道,一个是控制通道,一个是数据通道,两个通道共享一个底层socket,这基本就是一个大环境了。

      OpenVPN的manual中,明确指出除非万不得已不要使用TCP,因为会出现重传叠加的问题,事实上,TCP在传输纯粹的数据方面毫无优势,即使丢包或者乱序,第七层也会处理,但凡直接使用TCP的,都是有着严格的控制逻辑,比如协议处理等,而直接在第四层处理重传和按序要比第七层处理快捷得多。
      在OpenVPN中,其实本应该创建两个独立的socket,一个用TCP,一个用UDP,TCP处理控制通道,UDP处理数据通道,我相信,TCP处理SSL一定比UDP的可信层要好得多吧。

 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1343725