TCP/IP之NAT

NAT(Networking Address Translation)

NAT本质上是一种重复利用同一组IP地址的机制。它主要是为了解决俩个问题:
IP地址消耗和路由表的规模。
它通过NAT设备将网络划分为内网(private address realm)和外网(例如因特网),NAT设备给所属机器分配内网IP,在进出的流量经过时进行内外网IP地址和端口映射。
它的主要缺点是要进行特殊配置以使内外网可以通信。另外NAT还要修改每个包(packet)的地址信息以便进行正确的通信。
NAT还会给一些在应用层用到IP地址信息的应用协议造成问题,它们需要应用层网关函数来重写应用层内容以便可以和NAT一起使用。
一般来说NAT会进行转换(translation)和包过滤(packet filtering)两种操作。

传统NAT:基本NAT和NAPT

NAT准确的行为一直没有定,但是我们可以通过NAT实现的方式来进行分类。
传统NAT包含基本NAT(basic NAT)和NAPT(Network Address Port Translation)两种。基本NAT只重写IP地址,不常见(因为不显著解决IP地址消耗的问题)。NAPT重写IP地址和端口。NAPT通过传输层标识符(ports对于TCP和UDP,query identifier对于ICMP)来区分内网的哪个host和某个包(packet)是相关的。

左侧的基本IPv4 NAT 只重写IP地址,不重写端口号,所以我们看到有两个23479端口,造成了混乱。
右侧的NAPT,可以重写端口号,将另外一个23479重写成了3000。以使内外网IP地址和端口号能够一一对应。

为了防止内网和外网的混乱,比如内外网都有192.2.2.2这个网址,我们就无法区分这个是内网还是外网了,所以我们会给内网预留网段。分别为10.0.0.0/8,172.16.0.0/12和192.168.0.0/16。

NAT还有防火墙功能。默认NAT的内网一侧没法被互联网访问。内外网只有通过NAT才能进行通讯。NAT一般允许所有进出的通讯,但是会屏蔽掉所有外部新请求链接

NAT和TCP

首先我们想象一个场景,在下图中无线终端10.0.0.126访问web服务器 www.isoc.org(212.110.167.157)。

这个连接以ip:port的形式在无线终端可以写成
10.0.0.126:9200;212.110.167.157:80。在路由器(即NAT)发现是这个包是一个新的连接(有SYN标识),并在这里将源地址改为可路由的外部地址,所以当NAT转发包时地址对为63.204.134.177:9200;212.110.167.157:80。
除此之外,NAT会创建一个内部状态来记住一个新的连接被NAT处理,叫做NAT会话(NAT Session)。这个会话会至少保留一个条目(被称为NAT mapping)包含终端的源地址及端口号。
当服务器应答时,可以用终端指定的端口号进行通讯,这个叫做端口保留(port preservation)。然后NAT通过查询NAT mapping,将应答的包的目的地址从外网地址(212.110.167.157:80;63.204.134.177:9200)改写为内网地址和端口号(212.110.167.157:80;10.0.0.126:9200)并转发。
当TCP连接终止时,FINs被互相发送完成。会话被清除。
但是有些TCP连接非正常终止,当TCP发送SYN时,一个连接定时器(connection timer)会被激活,如果ACK返回时间超时,NAT会话被终止。如果ACK按时返回,创建 会话定时器(session timer),在session timer 超时后(即这段时间没有ACK等),NAT可能会发送包给内部节点,来检测是否TCP已终止,如果无反应,或者返回RST等,NAT会话被终止。

NAT和UDP

UDP在NAT上的问题和TCP差不多。但是UDP是无状态的,所以会给UDP添加一个mapping timer,一段时间这个映射没被用,就被清除掉。

但是有一点要注意,UDP和IP包可能被分片,但是除了第一片,其他的都没有端口号信息,NAPT也就没法处理。这也适用于TCP和ICMP。
总的来说分片无法被简单的NAT和NAPT处理。

NAT和隧道化的包(Tunneled Packet)

NAT不仅要重写IP的header,还要重写封装在其中的其他包的header或payload。

地址和端口映射行为

NAT的行为有相当大的差异,主要差异在于地址和端口映射的细节。

在上图当中我们用X:x来代表内网地址,X代表IP地址,x代表端口号。
为了能够访问外网的Y1:y1地址,NAT建立了一个映射IP地址为X1′,端口号为x1’。
假设接着访问外网的Y2:y2地址,NAT又建立了一个映射IP地址为X2’,端口号x2’。

一般来说X1’等于X2’,因为一般只有一个IP地址。
如果x1’等于x2′ ,我们称之为复用(reused)。如果x1’=x2’=x我们称之为端口保留,但是一般不可能,因为会有端口冲突。
对于大多数连接(包括TCP和UDP)来说终点独立(endpoint-independent)是需要的。
一个NAT可以有多个外部地址,这组地址被称作NAT 地址池(NAT address pool)。当一个内部host有多个连接,如果每个连接都被分配给同一个外网IP地址,我们称之为配对(pairing)。

过滤行为

一个NAT不仅进行IP地址和端口映射,如果充当防火墙角色的话还进行流量过滤行为。

NAT的过滤行为通常和是否建立了地址映射有关,没有映射的话NAT就不知道将外部的流量转发给内部哪个地址。
一般当绑定建立,过滤停止对返回流量过滤。
对endpoint-independent 类型的NAT,映射一旦建立,任何数据源的任何数据都可以流入。
对于Address-dependent类型的NAT,当Y1被X1:x1连接过,流量允许从Y1:y1流入。

对于Address-Port-dependent类型的NAT,当Y1:y1被X1:x1连接过,流量允许从Y1:y1流入。

NAT背后的服务器

但是当一个在NAT后面的host想对外提供服务时就出现了问题,首先上面我们讲到NAT不接受新入流量,因为没有映射,不知道转发给内网的哪个host,其次这个host的IP地址是内网地址,外部无法访问。
所以NAT的外部IP地址必须被使用使得server可以被找到,然后NAT必须将对应的流量重写并转发给server。这个过程被称作端口转发(port forwarding)。
端口转发一般需要静态配置服务器IP地址和需要被转发的端口号,端口转发指令就像一个预先配置好的静态NAT映射。

U型回路(Hairpinning 或 NAT LOOPBACK)

如果源地址和目的地址在同一个内网里,NAT通过U型回路(hairpinning or NAT loopback)来支持这种场景。

如果X1:x1只知道X2:x2的公网地址X2′:x2’,当X1试图建立连接时,NAT将流量直接转发给X2,那么问题来了,X2接收到的请求源地址是哪个呢。如果传给X2的是X1′:x1’,我们称这个NAT有外部源地址和端口 U型回路行为。这个行为是TCP NAT需要的。

NAT编辑器

如果一个NAT不仅能重写IP和TCP部分的数据报的IP地址和端口号,还能修改应用层的,我们称之为NAT编辑器(NAT editors).

NAT 穿透(NAT traversal)

有时应用想要完成自己的NAT穿透。这涉及到应用确定外部IP地址和端口号,当它穿过一个NAT时会根据这些相应的更改自己的协议操作。
如果一个应用被部署在不同网络中(有多个服务器和客户端),那么服务器可以用来给客户端传递数据,但是一般这是最后手段,一般会提供某种方式让客户端直连。
直连的方法在P2P文件分享,游戏等应用中很流行。但是这些技术都局限于特定的应用,也就是手每个需要NAT穿透的新应用都要实现自己的方法。这回导致冗余等问题。
为了解决这个问题一个标准的处理NAT穿透的方法被建立。它是基于几个截然不同的协议。

孔洞和冲孔(Pinholes & Hole Punching)

前面我们讲过NAT有改写及过滤功能。当一个映射建立后,它就会允许特定的应用进行双向通信。这种映射基本只存在于某个特定应用的运行期间,我们把这种映射叫做孔洞(pinholes)。
通过孔洞让两个或更多在NAT后面的系统通过孔洞(pinholes)直接进行通信的方法叫做冲孔(Hole Punching)。
冲孔的过程是这样的:一个客户端通过上行(outgoing)连接服务器在本地NAT建立了映射,另外一个客户端通过同样方式建立映射,这样服务端就有了这俩客户端的外网IP地址及端口号,如果所有的NAT都是endpoint-independent类型的,那俩客户端就可以直接连接了。

单边自我地址修复(UNilateral Self-Address Fixing UNSAF)

应用会通过一系列方法获取它们通过NAT时将要用的地址。这被称作修复地址信息。
非直接的方式是在通过穿过NAT交换通信(exchange traffic)来干涉NAT行为获取。
直接的方式是通过某种特定协议进行应用和NAT之间的直接转换。
IETF投入的巨大努力来进行非直接方式的研发,现在它们被广泛支持,比如著名的VOIP应用。
一个应用在没有NAT帮助的情况下修复自己的地址,被称之为单边自我修复(UNilateral self-address fixing)。
大多数情况下UNSAF以一种client/server形式进行操作,类似冲孔(hole Punching),但是有更加的的通用性。
但是如果有多层NAT网络,会增加复杂性,因为我们不知道需要的是哪个外网地址。

上图中的clientB,他通过server 2和server 1获取的外网地址是不一样的。

STUN(Session Traversal Utilities for NAT)

一种通常的UNSAF和NAT穿透的方式就是STUN。

STUN是由一个以前叫简单的UDP穿透NAT的隧道(simple tunneling of UDP through NATs)。现在被称为经典STUN。
STUN是一种相对简单的C/S协议,可以让客户端确定在其在NAT使用的外部IP地址及端口号。它可以通过发送keepalive信息来保持NAT的绑定。
总得来说UNSAF方法并不总是成功,但是它避免了修改网络路由器,应用协议或者服务端。只需要客户端实现STUN协议就可以了。
STUN通过UDP,TCP,TLS等进行操作。STUN基础协议有两种事物:请求/回应事物 (request/response transactions)和 指示事物(indication transactions)。指示事物不需要返回,C/S均可发。基本STUN的header的长度为20bytes,

所有的STUN信息包含类型,长度,magic cookie(值为0x2112A442),96-bit的事物ID(transaction ID),每个信息都以俩零开头,包含0~n个属性。
通过UDP/IP发送的STUN信息被认为不可靠的,所以STUN要有自己的重传机制,即超过RTT(round-trip time)后自定重传。
STUN的属性被以TLV(type,length,value)的形式编码,类型和长度均为16bit,值部分是变量长度(最多64kB),但是要通过乘4bit来填充低位。
属性的类型编码在0x8000以下的为必须属性(comprehension-required attribute),无法解析会返回错误,其他的为可选属性(comprehension-optional attribute)STUN的11个属性如下表上面属性中的异或地址(XOR-MAPPED-ADDRESS),是为了防止基本的应用层网关修改和重写IP地址等。

一个绑定请求的例子如图

首先是 Message type 属性,它是14bit的,这里转换成16进制的,对照可知STUN Methods属于 binding, 其中特殊的两bit标识事务(transaction)对照可知属于request,所以这个请求最终为 binding request,然后是message length 字段,固定的值magic cookie,随机的transaction id,包含 SOFTWARE属性,用来标识客户端应用。
然后回应如下图

除了和binding request相同的字段,还返回了很多属性,首先message type,我们可以看出属于 binding success response字段,然后MAPPED-ADDRESS和XOR-MAPPED-ADDRESS可以看到STUN服务端返回的客户端映射地址。这样我们就可以知道客户端的外网地址和端口了。

STUN除了可以进行地址修复外,还可以进行其他方法操作被称为机制(mechanisms),例如DNS 发现等。其中一个很重要的机制提供验证(authentication)和信息集成(message integrity),它有两种形式 短期证书机制(short-term credential mechanism )和长期证书机制(long-term credential mechanism)。

TURN(traversal using relays around NAT)

TURN(通过转播穿透NAT)给两个或更多隐藏于没有关系的NAT后面的系统一种通讯的手段。
作为最后的通讯手段,它用一个转播服务器在系统之间进行来回运输数据。

过程是这样的 client 请求TURN server ,并带上要访问的其他系统(通常叫peer)的相关信息,通过DNS NAPTR 来获取服务端地址及通讯协议。client获取 转播的地址及端口号。client 同时获取了服务端映射的外网地址及端口号。
需要转播地址的原因是peer A可以连接TURN server,和 client没有创建连接。
client通过TURN 命令来创建和维持TURN上的分配(allocations)。一个分配(allocation)类似多边NAT绑定(包含peer用来连接client的转播地址)。

TURN支持两种方式在client和peer之间进行复制数据。
一种叫发送和数据(Send and Data),是TURN标识符(indicator),没有认证(authenticated)。
另一种使TURN独有的概念:通道(channel)。通道用4-byte 头,不兼容通常被TURN用的STUN格式的消息。

TURN流程如下:

client通过TURN定义的STUN分配方法 发送获取分配的请求,如果成功服务端返回成功标识符,及分配的转播地址。如果没有足够的认证信息,请求会失败。
client要发送刷新信息来保持分配存活。分配在没刷新的情况下10分钟过期。
分配可以通过请求分配并将STUN LIFETIME 设置为0来清除。
分配用5元组(5-tuple)来表示:
客户端的5元组包含: client的传输地址及端口(即内网地址及端口),服务端传输地址及端口,客户端及服务端通信协议。
服务端的5元组是同样的,不过将client的传输地址及端口换成了服务端映射的地址及端口。
每个分配可能有0或者更多的相关权限,限制可以通过TURN连接的类型。

TURN加强了STUN,增加了6个方法,9个属性,6个错误返回。
6个方法及方法号为:Allocate (3), Refresh (4), Send (6), Data (7), CreatePermission (8), and ChannelBind (9),前两个创建并保持连接,Send & Data 传输数据,CreatePermission创建权限。

下面我们来看一下建立分配的具体请求
TURN的分配请求以STUN消息的形式传递。
接着我们服务端给了返回信息

服务端返回未授权信息,并在返回中增加了REALM及NONCE属性值以供客户端形成下次请求,并返回MESSAGE_INTEGRITY属性来供客户端验证是否信息被修改。
客户端添加验证信息再次请求

服务端然后返回成功,分配建立,客户端定时刷新。

ICE(interactive Connectivity Esablishment)

鉴于各种的NAT和各种的穿透NAT的机制,一种通用的设施(facility)叫做交互性连接创建被发明出来帮助基于UPD的应用穿透NAT创建连接。

 

新时代 IP 地址架构

距离TCP/IP详解第一版已经过去了几十年,互联网发生了翻天覆地的变化。下面我们就来看看IP地址方面有哪些变化

IP地址的表达

IPv4的表达大家应该很熟悉,比如192.168.1.1等,这种是点分十进制,还有一种是二进制表达(32位)

%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%887-47-37

  • 现在的流行趋势IPv6的表示方法大家可能还不是很熟悉,IPv6地址为128bit长度。常用的标记法为一系列4个16进制的数以冒号隔开。例如5f05:2000:80ad:5800:0058:0800:2023:1d71 ,每个被冒号隔开的称作block。看起来蛮复杂,所以IPv6有一些简化的规则:
  • 一个block的打头的零不需要写
  • 全是0的block可以省略,被记号::代替。2001:0db8:0:0:0:0:0:2被写为2001:0db8::2不过一个IPv6地址只能用一个::号。防止模糊。
  • 嵌套的IPv4的IPv6表现形式可以用混合标记法。表示方法为在IPv4地址前面加值ffff。例如::ffff:10.10.10.1代表IPv4地址10.10.10.1.这种地址叫做IPv4-mapped IPv6地址。
  • IPv4-compatible IPv6 地址,已废弃RF4291

IPv6的冒号经常会和端口号前面的冒号弄混,所以IPv6地址用中括号隔开例如这样:http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:443/

地址分类

在IP地址最初定义的时候,每个单播IP地址都有一个网络部分(网络号),来区分接口用的IP地址从哪个网络找。还有主机部分(主机号),用来表示网络上的某个特别主机。由此延伸出了我们熟悉的5类IP地址%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%888-49-38

随着新接入的网络段越来越多,分配新的网络号越来越麻烦。大家想找到一种方法实现一个连接着互联网的site能够被集中分配一个网络号,然后这个网络号再被site管理员本地划分为多个。这样就不用改变其他互联网核心的路由结构。

上面的划分都是在一个site的本地,对外它还是一个传统的A,B,C类型的网络。实现这个主意要有区分一个IP地址的网络部分和主机部分的能力。我们把这种方法叫着子网地址。

传统的网络分配是给你分配一个A,B,C类型的地址,然后网络号后面都是可以分配的主机号。现在我们要把主机号这部分进一步区分,分为子网号和主机号。
%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%889-12-14

宏观上看是如下图%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%889-13-17

由此出现一个问题,我们怎么区分一个IP地址哪部分是网络号和子网号,哪部分是主机号。 子网掩码就是为了解决这个问题

子网掩码

子网掩码的长度和IP地址长度一样(即IPv4是32位子网掩码,IPv6是128位)。
子网掩码的每个bit用1代表网络或子网,0代表主机,所以子网号又可以缩写为连续 1bits的个数,这种是目前最常见的格式,有时被称作前缀长度(prefix length)%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%889-53-27

可变长度子网掩码(VLSM)

现在我们允许不同长度的子网掩码应用在一个具有相同网络号的site的不同网络部分。%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%8810-00-02

子网掩码下面最多有多少主机号也很容易计算,比如掩码为/24,则32-24=8,2^8 为256个主机号。

CIDR和集合

CIDR(Classless Inter-Domain Routing)取消了过去那种按A,B,C分类的网络方式,因为这种方式很容易被分配光。CIDR 类似于VLSM,将网络分成了一个连续的地址,可包含255~65536个主机。网络范围并没有有像经典ABC网络一样事先指定,而是用一种类似子网掩码的CIDR 掩码。和子网掩码不同,它对互联网是可见的。所以互联网核心路由器要能处理网络号加CIDR掩码。称这种方式叫前缀。

一个n-bit的前缀被事先定义好,这个n在ip长度范围内,它一般附在base IP地址的后面用/号隔开。%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-23-%e4%b8%8b%e5%8d%8810-45-30

以128.0.0.0/24为例,前面128.0.0.0的前面24位要固定不变,后面8为可以变,所以它的地址范围就是128.0.0.0-128.0.0.255了。

聚合

随着互联网的不断发展,全球的路由表条数越来越多多,路由体验越来越差。

1970年代发明了层级路由(hierarchical routing):网络拓扑结构以树的形式来住在,IP地址被以拓扑敏感的方式分配,这样可以维持一个很小的路由,同时保持最短路径。

%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-25-%e4%b8%8b%e5%8d%889-21-08

左边IP地址被任意分配给一个路由,右边根据路由表的位置把相应的IP分配给它。
也就是说右边的最顶端的IP地址某一部分是固定的,它下面所有的路由表的IP地址,都是这个IP地址非固定部分的排列组合。

在因特网环境下,层级路由的概念可以用来缩小路由表的条数,通过一个叫路由聚合的过程来实现。

%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2016-11-25-%e4%b8%8b%e5%8d%889-31-34

在上图中190.154.27.0/26 和190.154.27.64/26 如何展开成二进制形式就可以看出来

190.154.27.0000 0000 和 190.154.27.0100 0000 我们可以看出是第26位不一样,也就是说这两个IP地址都是190.154.27.0000 0000从第26下面的排列组合,所有这俩地址可以合并为190.154.27.0/25, 这个25我们前面讲过是CIDR掩码。代表IP地址的前25位bit都是固定的。
其他IP地址的聚合也都是同理。最终我们完成了路由聚合。

TCP/IP详解之DHCP(IPv4)

DHCP(Dynamic Host Configuration Protocol)是用来将一些配置信息(如IP地址,子网掩码,路由器地址等)分配给主机网络接口用的。

DHCP是由BOOTP(Internet bootstrap Protocol)发展出来的。DHCP 沿用了BOOTP的租赁(leases)这一概念。就是这些分配给主机的配置信息只在约定的时间范围内有效。DHCP和BOOTP是兼容的,他们都使用UDP/IP协议传输数据,客户端使用68端口,服务端使用67端口。

DHCP主要包含两部分:地址管理和配置信息传递。
DHCP提供3种地址分配方式:自动分配,动态分配,手动分配。

动态分配:从IP池当中取出可重置的IP分配给主机。(最主要的分配方式)

动态分配:从IP池当中取出不可重置的IP分配给主机。

DHCP的租赁时间主要是主机数和IP池大小之间的平衡,更长的租赁时间会简化网络,增加网络稳定性,但容易耗尽IP池。

DHCP协议的各个字段

屏幕快照 2016-10-24 下午11.11.41

  • OP(operation)1代表请求,2代表回复
  • HW type(hardware type)1代表Ethernet
  • HW Len (hardware length)用来存储硬件的mac地址,通常为6位
  • Hops 跳数,用来存储这条信息被转播的次数。
  • transaction ID 标识符,由客户端产生,用来匹配回复。
  • secs 已经租赁的时间。
  • Flags 1bit已经定义好的广播flag,被设置后只接受广播,不接受带IP的单播
  • client IP地址没有的话为0
  • “your”ip address字段,代表服务端提供给客户端的IP地址。
  • (Next)server IP address 下个用来客户端bootsrap的服务端IP(如用来下载操作系统)
    还有很多option选项,如

    Subnet Mask (1), Router Address (3), Domain Name Server (6), Domain Name (15), Requested IP Address (50), Address Lease Time (51), DHCP Message Type (53),等

下面我们看看DHCP是如何传递的

屏幕快照 2016-11-01 下午7.32.10

  1. 首先client 发送一个DHCPdiscover 的广播信息(在协议的option选项里面DHCP Message Type)
  2. 收到discover广播的DHCP服务器发送一个DHCPoffer信息,提供一个可用的ip地址,及租赁时间(T),renew 时间(T1) rebind 时间(T2)通常T1 = (T/2) , T2 = (7T/8).
  3. 接受到一个或多个offer后client决定用哪个后,发送一个DHCPrequest 的广播消息(包含选项中的server identifier)。
  4. 被选中的server将地址绑定到永久存储,其他清除掉关于这个请求的状态。被选中sever接着会发送一个DHCPACK信息。
  5. 客户端收到ACK信息后,检测是否有IP冲突,如果不可用发送DHCPDecline信息。
  6. 另外如果client在IP地址失效前放弃,它可以发送一个DHCPRELEASE信息。
  7. 如果client已有ip地址,只是想续租IP地址,可以跳过第一步。

wireshark下DHCP的消息如下

屏幕快照 2016-11-01 下午7.56.59

TCP数据传输之成块的数据流

正常的数据流

屏幕快照 2016-07-10 上午10.11.46

上图有几个需要注意的地方

报文7  直接确认了4,5两条报文。这是使用了一种叫做‘隔一个报文段确认’的策略。使用TCP的滑动窗口协议时接收方不必确认每一个收到的分组。

快的发送方和慢的接收方

屏幕快照 2016-07-10 上午10.27.12

4-7 发送了 4条报文,8 返回了一条ACK,但是更新窗口大小为0 ,表明接收到了数据,但是还没来得及处理。然后9又发送了一条ACK,将窗口更新为4096,它并不用来确认任何数据,只是用来正价窗口的右边沿,因此被称为窗口更新。

滑动窗口

屏幕快照 2016-07-10 上午10.35.28

接收方通告的窗口称为提供的窗口。上图为4-9区域,说明接收方确认了1-3数据,并通报窗口大小为6.

窗口的运动
屏幕快照 2016-07-10 上午10.39.23

首先明确一点窗口不仅有大小,还有位置,这是用来表明不同时间,窗口的状态。

  1. 窗口的左边沿向右边沿靠近称为窗口合拢。发生在数据被发送和确认时。
  2. 窗口右边沿向右移动时称为窗口张开。发生在另一端的接收进程读取已确认的数据并释放了TCP的接收缓存时。
  3. 当右边沿向左移动时,我们称为窗口收缩。这种情况比较少。
  4. 如果左边沿到达右边沿称为零窗口
  5. 发送方不必发送一个全窗口的数据。
  6. 改变发送方和接收方缓存大小,会影响吞吐量。

PUSH标志

目前PUSH标志一般由TCP实现自行决定何时设置标志。PUSH标示本身代表尽快将数据传送给上层数据的问题。

慢启动

慢启动算法:通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。

慢启动为发送方TCP增加了一个窗口:拥塞窗口(congestion window)记为cwnd。

  • 初始化cwnd为1.
  • 每收到一个ack cwnd 加一。
  • 当cwnd过大时,中间路由器开始丢弃分组。这就通知发送方拥塞窗口过大要调整。

成块数据的吞吐量

屏幕快照 2016-07-10 上午11.28.43

时间12和13接收方产生俩个ack,这两个返回到发送方的ACK之间的间隔与报文段之间的间隔一样。被称为TCP的自计时。(这个好像和经受时延的确认有点冲突?)

注意上图截止到时间15,拥塞窗口cwnd大小已经变为了4.

屏幕快照 2016-07-10 上午11.35.46

时间31,发送方和接收方之间的管道(pipe)被填满。不管有多少报文填充了这个管道,返回路径上总是具有相同数目的ACK。这就是连接的理想稳定状态。

带宽时延乘积

通道的容量为

capacity(bit) = bandwidth(b/s)*round-trip time (s)

一般称之为带宽时延乘积。

紧急方式

 

 

 

TCP 数据传输之交互数据

什么是交互式数据

交互式数据就是和服务端交互的一些数据。比较古老的有Rlogin啥的,现在比较常用的比如SSH。客户端在敲击键盘时要传送给服务端,然后服务端确认并回显。

经受时延的确认

通常TCP在接收数据时并不立即发送ACK,而是等待200ms左右将同样需要传给另外一端的数据一起发送。 如果200ms还没有同方向数据就单独发送,这种ACK称为经受时延的确认。

Nagle算法

该算法要求一个TCP连接最多只能有一个未被确认的为完成的小分组。

就是上一条数据的ACK没发过来的过就不能再发送数据了。

在跨广域网运行一个交互应用的环境下,当进行多字节的按键输入时,默认使用Nagle算法会引起额外的时延。

窗口大小通报

在下一篇有关滑动窗口协议时统一写吧。

看书好像有好多内容,一总结全没了啊,就这样吧。

TCP连接的建立与终止

连接的建立与终止

 

屏幕快照 2016-06-29 下午11.10.29

从上图用tcpdump针对ftp连接的抓包可以看到一些数据。

可以看到ip首部的一些数据,tos 为0 表示一般数据。ttl 表示生存时间为64跳(hop)。 标识字段为33125,偏移量为0 标志中有DF(don’t fragment) ,表示不分片。

对于TCP部分 每个输出行按如下格式展示:

源>目的:标志

这里的标志代表TCP首部的6个标志比特中的4个

屏幕快照 2016-06-29 下午11.27.33

最上面的图中的S 就是同步序号,代表建立连接。

首图中的 seq 2140943953 其实是省略了。正确的格式是开始的序号,一个冒号,隐含的结尾序号。如下图中的:

屏幕快照 2016-06-29 下午11.44.16

不过第一版书中(0)代表数据字节数,根据我实际测试,现在应该改为直接在尾部显示length 0了。

屏幕快照 2016-06-29 下午11.47.15

上图分别是两端对同步序号报文的反馈。  ack表示确认的字段 可以看到值为1 ,这个要注意:对所有数值很大的序号进行排序是很麻烦的,因此默认情况下tcpdump只在显示SYN报文段时显示完整的序号。而对其后的序号则显示他们与初始序号的偏移量。 所以这个最终值其实是 SYN序号+1。

cksum字段表示校验和,win表示窗口大小。

为了建立连接要进行三次握手:

屏幕快照 2016-06-29 下午11.58.50

连接终止:

建立一个连接要三次握手,终止一个连接要4次握手

连接超时:

屏幕快照 2016-07-01 下午10.07.56

从上图可以知道大约1s左右超时一次。

TCP 半关闭
TCP提供了连接的一端主动关闭后还能接收来自另外一端的数据的能力。

屏幕快照 2016-07-01 下午10.17.52

TCP状态图:

屏幕快照 2016-07-01 下午10.19.16

2MSL(maximu segment lifetime)

应用主动关闭才会进入 2MSL ,被动关闭会走 close_wait .

发送过对方的fin的ack 才会进入2MSL状态。

需要2MSL状态有两个原因,一个是防止迟到报文被错认为下个连接的报文。

二是因为TCP是双工的,如果最后的ack没有发送成功,另外一端会重新发送FIN,我们这一端可以重新发送ACK。

TIME_WAIT-why-thumb-500x711-277

网上找的图,不过我感觉上边的虚线应该是忘上一点,到ack的刚发送后,不知道理解对不对。

一个插口对在它处于2MSL的等待时,将不能再被使用。因为服务端的端口号通常固定的,如果服务端主动断开连接进入2MSL,那好多客户程序如果端口号没变的话就连接不上了。但许多具体实现允许一个进程重新使用仍处于2MSL等待的端口(通常设置选项SO_REUSEADDR)。另外还有一些实现允许一个新的连接请求到达仍处于2MSL的连接,只要新的序号大于该连接前一个替身(一个连接新的实例叫替身)的最后序号。

TCP平静时间

如果使用处于2MSL等待端口的主机出现故障,他会在MSL秒内重启。TCP在重启后的MSL秒内不能建立任何连接, 这段时间称为平静时间。

FIN_WAIT_2 状态

发送FIN,对方ACK。还没收到对方的FIN会进入这个状态。

复位报文

无论何时一个报文发往基准的连接出现错误,tcp都会发出一个复位报文段。

异常终止一个连接

当一端异常终止连接时会发送一个RST报文段。

半打开连接

如果一方已经关闭或异常终止而另一方还不知道,我们将这样的连接称为半打开。

DNS 域名系统

DNS基础

以“.”结尾的域名称为完全合格的域名FQDN(full qualified domain name);

顶级域名分为三部分

  • arpa是一个用作地址到名字转换的特殊域。
  • 7个3字符长的普通域。也称为组织域。
  • 所有2字符长的域是国家域。

屏幕快照 2016-06-22 下午9.37.34

一个独立管理的DNS子树称为一个区域(zone)

一个区域的管理者鼻血为该区域提供一个主名字服务器和至少一个辅助名字服务器。

主服务器从磁盘文件中调入该区域所有文件,辅名字服务器从主服务器调入所有信息。

辅服务器从主服务器调入信息称为区域传送。

DNS一个基本特征是用超高速缓存。

DNS报文格式

屏幕快照 2016-06-22 下午9.45.25

 

标识由客户端程序设置。客户通过他来确定响应与查询是否匹配。

16bit标志分为若干字段。

屏幕快照 2016-06-22 下午9.48.15

qr 0 表示查询,1表示响应。

opcode 0 标准查询,1反向查询,2服务器状态查询。

AA 授权回答

TC 可截断

RD 期望递归。告诉名字服务器必须处理这个查询。也称为递归查询。

RA 可用递归。除根服务器,大多数名字服务器支持递归查询。

rcode 返回码 0 无差错,3有差错。

上图中问题部分。

屏幕快照 2016-06-22 下午9.54.34

查询名是域名。 查询名是多个标识符的序列。每个标识符以首字节的计数值来说明随后标识符的字节长度。每个名字以最后字节为0结束。

屏幕快照 2016-06-22 下午9.54.42

查询类型可以是IP地址,CNAME,等。 查询类通常为1,表示互联网地址。

DNS响应报文资源记录部分

屏幕快照 2016-06-22 下午10.07.17

主机名查询

指针查询

资源记录。

高速缓存。

 

 

udp 协议

udp 协议 是在运输层中面向数据报的协议。我本来以为没啥用,后来发现voip什么的就是基于udp的所以还是写篇博客总结一下吧。

UDP首部

屏幕快照 2016-06-19 下午4.35.00

端口号代表发送进程和接收进程。

udp的长度指的是upd首部和udp数据的长度。该字段最小值为8字节。

UDP校验和

udp校验和覆盖udp首部和udp数据。和ip首部的校验和只覆盖ip的首部有所区别。

upd数据报的长度有可能为奇数字节,但是校验和算法是把16bit字节相加。解决方法是在后面补0.

udp数据报和tcp段都包含一个12字节的伪首部。他是为了计算校验和而设置的。为首部包含ip首部的一些字段。其目的是让UDP两次检查数据是否已经正确的目的地。

屏幕快照 2016-06-19 下午5.11.57

首先UPD伪首部在实际运输中并不存在。那源IP地址和目的IP地址是怎么来的,我觉得分两种情况,一种校验和是在IP网络层中计算的,我觉得不大可能。另一种是在运输层中,UDP在运输层就可以获得IP地址。通过上网查找得知确实是第二种。理想化的模型往往都是逻辑上的。真正实现起来很多地方要模糊一些。况且协议栈一般在内核中运行,效率是很重要的。其实运输层和网络层的实现是紧密关联的,在udp层拿到IP信息其实不是什么麻烦事。以FreeBSD的实现来说,各层之间传递的数据都是同一个package buffer(即mbuf)的指针。IP层会按IP的偏移量去处理数据。UDP层就用UDP层的偏移量处理数据。这样能省掉相当多的内存拷贝。

UDP的校验和是一个端到端的校验和。它由发送端计算,然后接收端验证。其目的是为了发现UDP首部和数据在发送端到接收端之间发生的任何变动。

UDP的校验和是简单的16bit和,他们检测不出交换两个16bit的差错。

IP分片

物理网络层一般要仙子每次发送数据帧的最大长度。IP层接收到数据报时,要判断通过哪个接口传输,并获取其MTU,超过就要分片。

分片可能不只一次。每一片都有IP首部,通过IP首部中的标志字段的片偏移字段组合起来。也就是说UDP首部只有第一片有。

IP彪子字段,其中一个比特用来表示“更多的片”,除最后一片外,其他都要将该比特置为1.起中一个比特用来表示片偏移字段,最后一个比特是“不分片”位。如果将这个比特置为1,则IP将不对数据报进行分片。丢弃数据报并发送一个ICMP差错报文。

UDP和ARP

我们知道ARP存在于链路层,当发送一个UDP请求是先将数据添加UDP首部,接着要添加IP首部,传到链路层时要添加链路层的首部,包含IP地址对应的物理地址等,如果ARP缓存里面没有对应的地址,就需要ARP发送一个广播,让有对应iP的接口返回真实的物理地址。在大多数的实现中,在等待一个ARP应答时,只将最后一个报文发送给特定的目的主机。

最大的UDP数据报长度

理论上IP数据报最大长度是65535字节,这是由IP首部16比特总长度字段所决定的。

所有去除20字节IP首部,8字节UDP首部,UDP数据报理论上最长为65507.但大部分实现比这个长度小。

ICMP源站抑制差错

当一个系统接收数据报的速度比其处理快时可能产生这个差错。

比如一个路由器连接以太网和SLIP链路,从以太网接收的数据太快,而SLIP的速度只有以太网千分之一,那么路由器就可能返回“源站抑制(source quench)”错误。

 

 

 

tcp/ip 协议 第三到十章

目前感觉这几章相对底层,合并为一篇总结。

ARP(地址解析协议)

他的功能是在32bit的IP地址和采用不同网络技术的硬件地址之间提供动态映射。

即IP运输层和数据链路层使用的任何类型的地址。ARP的分组如下图:

屏幕快照 2016-06-13 下午10.05.17

这个就是ARP请求和应答分组的格式。

当目的地址全为1时为广播地址(ff:ff:ff:ff:ff:ff)

ARP代理

如果ARP的请求是从一个网络的主机发往另外一个网络上的主机,那么连接这两个网络的路由器就可以回答该请求,这个过程称作ARP代理。

RARP

RARP用于无盘系统。网络上的每一个系统都有唯一的硬件地址,他是由网络接口生产厂家配置的。无盘系统的RARP实现过程是从接口卡上读取唯一的硬件地址。然后发送一份RARP请求,请求主机相应该无盘系统的IP地址。

简单的来说就是无盘系统获取IP地址用的。

ICMP(Internet control message protocol)

它用来传输IP差错报文和其他需要注意的信息。是IP协议的附属协议。主要被IP层使用,也会被应用程序访问。ping和traceroute都使用了ICMP。

IP选路

选路原理如下:

  • 搜索匹配的主机地址
  • 搜索匹配的网络地址
  • 搜索默认选项

动态选路协议

 

tcp/ip 协议 第三章 ip 协议

IP 首部

上一节我们讲到数据在传输过程中会封装,在运输层里的ip协议会添加IP首部。主要包含4位版本号,4位首部长度,4位TOS(type of service),4bit分别带别最小时延,最大吞吐量,最高可靠性,最小费用,4bit中只能置其中1bit,如果都为0则为普通服务。ttl(生存时间),32位源ip地址,32位目的ip地址等。

屏幕快照 2016-06-10 上午10.06.03

IP路由选择

  1. 首先检查目的地址是否为本机的IP地址之一或IP广播地址。如果是数据报送到由IP首协议字段指定的协议进行处理。
  2. 如果不是,如果IP层被设置为路由器的功能进行转发,否者丢弃。 路由表有如下信息
    • 目的IP地址,是一直不变的。他可以是一个完整的主机地址,也可以是一个网络地址。如果网络地址中的主机号为0,则为特定网络的所有主机。
    • 下一跳路由器(next-hop router)的IP地址,或有直接连接的网络IP地址。在这过程中,链路层的地址是变化的。
    • 标志。其中一个标志指名目的IP地址是网络地址还是主机地址,另一个标志指名下一站路由器是否真正为下一站路由器还是一个直接相连的接口。
    • 为数据报的传输指定一个网络接口。
  3. IP路由的选择是逐跳的(hop-by-hop),IP并不知道到达任何目的地的完整路径。
  4. IP路由功能如下:
    • 搜索路由表,寻找能与IP地址完全匹配的表目。
    • 搜索路由表,寻找与目的地匹配的表目。
    • 搜索路由表,寻找标为“默认”的表目。

子网寻址

现在所有的主机都要求支持子网编址,不是把IP地址看成由单纯的网络号和主机号组成,二是把主机号再分成子网号和主机号。子网划分缩减了路由表的规模。

屏幕快照 2016-06-10 上午10.10.00

子网掩码

除IP地址之外,主机还需要知道有多少比特用于子网号及多少比特主机号,知识通过子网掩码来确定的。这个掩码是个32bit的值,其中值为1的比特留给wangluohao和子网号,0的比特刘伟主机号。这个真是解了我多年的疑惑,以前还用window时就经常碰到这个子网掩码,完全不知道干啥的

屏幕快照 2016-06-10 上午10.27.08

上图下面那个错了,主机号应该是6位。

特殊的IP地址

0表示所有比特位为0,-1表示所有比特位全为1;netid,subnetid和hostid分表表示不全为0或全1的对应字段。子网为空表示没进行子网划分

屏幕快照 2016-06-10 上午10.30.26

iiconfig 命令, netstat命令。