Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

TCP/IP协议栈学习笔记

30
Mar
2012

TCP/IP协议栈学习笔记

By Alex
/ in Network
/ tags DNS
0 Comments
名词术语
术语 解释
bogon

这个单词从bogus衍生而来,字面意思是伪造的
在TCP/IP协议中,bogon代表那些保留或者未分配的IP段,这些地址不应该出现在因特网上

由于ISP路由过滤、或者恶意软件的原因,Bogon地址的IP数据报可能到达目标机器上,目标机器往往进行Bogon地址过滤

Bogon地址和保留私有地址不是一个概念,但是某些软件可能把保留私有地址作为Bogon看待:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ip firewall address-list
# 输出
# add list="BOGONS" address=0.0.0.0/8
# add list="BOGONS" address=10.0.0.0/8
# add list="BOGONS" address=100.64.0.0/10
# add list="BOGONS" address=127.0.0.0/8
# add list="BOGONS" address=169.254.0.0/16
# add list="BOGONS" address=172.16.0.0/12
# add list="BOGONS" address=192.0.0.0/24
# add list="BOGONS" address=192.0.2.0/24
# add list="BOGONS" address=192.168.0.0/16
# add list="BOGONS" address=198.18.0.0/15
# add list="BOGONS" address=198.51.100.0/24
# add list="BOGONS" address=203.0.113.0/24
# add list="BOGONS" address=224.0.0.0/3
Reserved Addresses

即保留私有地址,这些地址用于特殊用途,例如:

  1. 多播地址,这类地址为224.0.0.0/4
  2. 用于本机的环回地址,这类地址为127.0.0.0/8
  3. Link-local address:用于两台主机在没有(被DHCP)分配IP地址的情况下通信,这类地址是169.254.0.0/16
  4. 用于私有网络(局域网)的本地通信,这类地址包括10.0.0.0/8、172.16.0.0/12、192.168.0.0/16
  5. 其它特殊用途
简述

11111111 = 377 = 255 = FF

网络协议分层

TCP/IP是四个层次上的多个协议的组合:

  1. 链路层:设备驱动程序与网卡一起处理与电缆(或其它传输媒介)的物理接口细节。某些链路层协议与物理层密切相关,有些则不然
  2. 网络层:处理分组在网络中的活动,例如分组的选路。包括 IP协议(网际协议), ICMP协议(Internet控制报文协议),以及IGMP协议(Internet组管理协议)
    1. IP协议:尽可能快的把分组从源结点送到目的结点,但是并不提供任何可靠性保证
    2. ICMP:IP协议的附属协议。 IP协议用它来与其他主机或路由器交换错误报文和其他重要信息
    3. IGMP:用来把一个 UDP数据报多播到多个主机
  3. 传输层:为两台主机上的应用程序提供端到端的通信。包括TCP(传输控制协议)和 UDP(用户数据报协议)
    1. TCP协议:为两台主机提供高可靠性(通过超时重传、发送和接收端到端的确认分组机制来保证)的数据通信。它把应用程序交给它的数据分成合适的小块交给下面的网络层、确认接收到的分组、设置发送最后确认分组的超时
    2. UDP协议:为应用层提供一种非常简单的服务:把称作数据报的分组,从一台主机发送到另一台主机,但并不保证该数据报能到达另一端
  4. 应用层:负责处理特定的应用程序细节。包括多种协议,例如:
    1. Telnet远程登陆协议
    2. FTP文件传输协议
    3. HTTP超文本传输协议
    4. SMTP简单邮件传送协议
    5. SNMP简单网络管理协议

其中底下3层主要由操作系统内核负责。应用层由应用程序负责。链路层处理了通信媒介的细节,应用层处理了特定用户应用程序(FTP、 Telnet等),网络层、传输层存在的价值何在?要理解这一点,必须考虑多个网络互联(网络的网络,互联网)的情况。可以使用以下方式把网络进行互联:

  1. 路由器(IP Router),在网络层上对网络进行互连。路由器可以为不同类型的物理网络提供连接,包括以太网、令牌环网、FDDI等。广义上说,任何具有多个网络接口 ( multihomed )的主机都可以作为路由器使用
  2. 网桥:在链路层上对网络进行互连。使得多个局域网组合在一起,这样对上层来说就好像是一个局域网

TCP/IP网络各层之间对等实体交换的单位信息称为协议数据单元(Protocol data unit,PDU)。PDU中的载荷部分称为服务数据单元(Service data unit,SDU)。除了物理层以外,上层协议的PDU都是传递给下层作为其SDU,由下层代为完成交换的。

五类IP地址

互联网络信息中心(InterNIC)负责管理IP地址,它只负责分配网络号

IPv4地址的结构如下图:

ip-addr-type

IPv4地址分类范围如下图: ip-addr-type2

从IP目的地类型来看,IP地址可以分为三类:

  1. 单播地址(目的为单个主机)
  2. 广播地址(目的端为给定网络上的所有主机)
  3. 多播地址(目的端为同一组内的所有主机)

此外,A/B/C类地址中均有一部分地址供局域网内部使用,称为私有地址(Private Address):

  1. A类私有地址:10.0.0.0 至 10.255.255.255
  2. B类私有地址:172.16.0.0 至 172.31.255.255
  3. C类私有地址:192.168.0.0 至 192.168.255.255

私有地址不会出现在Internet上。

域名系统

应用程序可以调用标准库函数来查看给定名字的主机的IP地址。大多数使用主机名作为参数的应用程序也可以把IP地址作为参数。

数据包封装

通过TCP协议传送数据时,数据被送入协议栈中,每一层协议对收到的数据都要增加一些头/尾部信息。TCP传给IP的数据单元称作TCP报文段(TCP segment);IP传给链路层的数据单元称作IP数据报(IP datagram)。通过以太网传输的比特流称作帧(Frame),以太网数据帧的物理特性使其长度必须在 46~1500字节之间。数据包封装过程如下图:

tcp

通过UDP协议传送数据时类似,只是UDP传给IP的数据单元叫UDP数据报(UDP datagram)。UDP头部为8字节。

IP数据报的头部包含8bit的数值,称为协议域,用于表示上层是何种协议,其中:1表示为ICMP协议, 2表示为IGMP协议, 6表示为TCP协议, 17表示为UDP协议。

由于很多应用程序可以使用TCP、UDP来传送数据,故TCP和UDP都用一个16bit的端口号(0-65535)来表示不同的应用程序,TCP、UDP将源、目标端口号均存放在报文头部。

以太网头部也包括16bit的桢类型域,用于区分 IP、 ARP还是ARP数据。

数据包分用(Demultiplexing)

当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。具体参考下图,请注意:很难严格的把某些协议划分到特定网络层次中。Demultiplexing

客户端-服务器模型

大部分网络应用程序在编写时都假设一端是客户,另一端是服务器,其目的是为了让服务器为客户提供一些特定的服务。
可以将这种服务分为两种类型:重复型或并发型。

端口号

服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23。这些知名端口号由IANA管理。
客户端通常对它所使用的端口号并不关心,只需保证该端口号在本机上是唯一的就可以了。客户端口号又称作临时端口号。

API

使用TCP/IP协议的应用程序通常采用两种应用编程接口:

  1. Socket:有时称作“Berkeley socket”,因其从伯克利版发展而来
  2. TLI:运输层接口,有时称作XTI。起初由AT&T开发
链路层

在TCP/IP协议族中,链路层的主要职责为:

  1. 为IP协议发送IP数据报
  2. 为ARP协议发送ARP请求与应答
  3. 为RARP协议发送RARP请求与应答

TCP/IP支持多种链路层协议,这取决于网络硬件,例如:

  1. 以太网
  2. 令牌环网
  3. FDDI
  4. RS-232串行线路

链路层的PDU被称为帧(Frame),其长度取决于具体的接口。

以太网和IEEE 802协议

以太网指的是DEC、Intel、Xerox等公司联合发布的一种标准,是目前TCP/IP局域网主要使用的技术。使用CSMA/CD(带冲突检测的载波侦听多路接入)作为媒体接入方式。以太网IP数据报的封装在RFC 894中发布。

IEEE 802委员会发布了与以太网稍微不同的标准集,包括802.3、802.4、802.5。它们的帧格式与以太网不一致。IEEE 802网络的IP数据报封装在RFC 1042中发布。

下图是以太网、IEEE 802封装格式的比较:

ieee802

在以太网中,主机之间通过交换以太网帧(Frames)来交互,每个主机以MAC地址作为唯一标识。现代NIC支持编程修改MAC地址。

以太网中的每个主机,都可以向任何其他主机直接发送帧。此外,主机还可以向特殊的地址ff:ff:ff:ff:ff:ff发送帧,所有主机都会收到,这种帧叫做广播。ARP、DHCP是两个重要的使用广播的协议。由于以太网支持广播,因此单个以太网也被称为广播域(broadcast domain)

当NIC接收到帧后,它会检查目标MAC地址是否匹配本机地址(或者是广播地址)。如果部匹配,默认情况下会丢弃。如果NIC配置为混杂模式(Promiscuous mode),则它不会丢弃不匹配的帧,而是全部交给操作系统处理。

串行线路IP(Serial Line IP,SLIP)协议

SLIP是一种在串行线路上对IP数据报进行封装的简单形式,在RFC 1055中发布。SLIP适用于RS-232、调制解调器接入Internet。SLIP封装的规则如下:

  1. IP数据报以END字符(oxc0)结束,为了防止噪声干扰,大部分实现在数据报开始处也传送一个END字符
  2. 如果IP数据报中存在END字符,以连续两字节0xdb 0xdc转义。0xdb作为SLIP的ESC字符
  3. 如果IP数据报中存在SLIP的ESC字符,则以连续两字节0xdb 0xdd转义

该封装格式有以下缺陷:

  1. 每一端必须知道对方的IP地址,没有办法把IP地址传送给对方
  2. 数据帧中没有类型字段,如果某条串行线路用于SLIP,则不能同时使用其它协议
  3. 数据帧上没有CRC,如果报文因为线路噪声出错,则只能有上层协议发现

SLIP还具有压缩的变种CSLIP。

PPP(点对点协议)

PPP被用在许多类型的物理网络中,包括串口线、电话线、中继链接、移动电话、特殊无线电链路以及光纤链路。

PPP还用在互联网接入连接上(宽带)。互联网服务提供商(ISP)使用PPP为用户提供到Internet的拨号接入,这是因为IP报文无法在没有数据链路协议的情况下通过调制解调器线路自行传输。PPP的两个派生物PPPoE(PPP over Erhernet,基于以太网的PPP)和PPPoA被ISP广泛用来与用户创建数字用户线路(DSL)Internet服务连接。

PPP被广泛用作连接同步和异步电路的数据链路层协议,取代了陈旧的串行线路IP协议(SLIP)。

PPP被设计用来与许多网络层协议协同工作,包括网际协议(IP)、TRILL、Novell的互联网分组交换协议(IPX)、NBF以及AppleTalk。

PPP数据帧的格式如下图:

PPP

标志字符0x7e需要进行转义,同步链路、异步链路使用不同的方式。

比起SLIP,PPP具有以下优点:

  1. PPP支持在单根串行线路上运行多种协议,不单单是IP协议
  2. 每一帧都有循环冗余检验
  3. 通信双方可以进行IP地址的动态协商(使用IP网络控制协议)
  4. 与CSLIP类似,对TCP和IP报文首部进行压缩
  5. 链路控制协议可以对多个数据链路选项进行设置
环回(Loopback)接口

环回接口可以认为是链路层的一种实现,通常作为TCP/IP协议栈实现的内置组件。 大部分系统支持环回接口,以允许同一台主机上的客户端/服务器可以通过TCP/IP通信。A类地址的127网络号专门供环回接口使用。

大部分系统把地址127.0.0.1分配给环回接口,并命名为localhost,传给环回接口的IP数据报不会在任何网络上出现。

在Windows中,通过ipconfig看不到此环回接口;在Linux中,通过ifconfig通常可以看到一个名为lo的网络接口,此即环回接口。

在Windows中可以配置一个虚拟的环回网卡(Microsoft  loopback adapter),主要用于在没有网络环境的情况下进行测试,环回网卡与TCP/IP协议中的环回接口不是一个概念。可以分配任何除了127.0.0.1以外的任何私有地址给环回网卡。

最大传输单元(MTU)

以太网、802.3、FDDI、PPP对数据帧的长度均有限制,分别为1500、1492、4352、296。链路层的这一特性称为MTU。
如果IP数据报的数据长度大于MTU,那么IP层必须对数据报进行分片(fragmention),使每一片小于MTU。

路径MTU

如果两台主机通信需要跨越多个网络,则每个网络可能有不同的MTU,那么路由中最小的MTU称为路径MTU。
由于路由选路不是对称的,因此A到B的MTU可能与B到A的MTU不同。

IP协议(网际协议)

IP是TCP/IP协议族中最为核心的协议,TCP、UDP、ICMP、IGMP等协议均要通过IP数据报的格式进行传输。IP协议是不可靠(Unreliable)的,即不保证数据报能到达终点,可靠性必须由上层协议保证(出错时仅仅会发送ICMP报文通知源主机)。

IP是无连接(Connectionless)的,即IP协议不维护任何关于后续数据报的状态信息,每个数据报的处理均是独立的。IP数据报可以不按发送顺序来接收。

IP层的每个PDU被称为IP数据报(IP Datagram)。

IP数据报头部

一般的,IP数据报头部为20字节(除非包含选项字段),如下图:IP-head

详解如下:

  1. 4位版本号:版本号为4或者6,代表IPv4或者IPv6
  2. 4位首部长度:即首部占有32bit(4字节长度),由于该字段为4bit,故首部最长为15*4=60字节。普通IP数据报头(无选项),该字段为5
  3. 8位TOS:由以下内容组成:
    1. 3bit的优先权子字段,现已被忽略
    2. 4bit的TOS子字段,4位分别表示:最小延时、最大吞吐量、最高可靠性、最小成本,这4位只能有一个为置1
    3. 1bit的未使用位,必须置0
  4. 16位总长度:整个IP数据报的长度。利用总长-首部长,可以得到数据部分的偏移量。由于该字段为16位,因此IP数据报最大65535字节。
  5. 16位标识:主机每发一份数据报,该值增加1。其后的3位标志、13位片偏移和分片、重组有关
  6. 8位TTL:生存时间,标识数据报最多可以经过的路由数。其初始值由源主机设值(通常32或者64),一旦经过一个路由器,其值减一,如果为0,数据报就被丢弃,并发送ICMP报文给源主机
  7. 8位协议:指明上层协议的类型。1表示为ICMP协议, 2表示为IGMP协议, 6表示为TCP协议, 17表示为UDP协议
  8. 16位首部校验和:用于验证IP数据报的有效性。如果出错不会发送ICMP差错报文,由上层发现丢失并重传
  9. 32位源、目的IP地址
  10. 选项:是数据报中的一个可变长的可选信息,必须是32bit的整数倍(不足插0),以保证IP首部始终是4字节的整数倍

IP数据报以大端(Big-endian)方式传输,即,对于上图的每行0-31(最低位0,最高位31),首先传送0-7,然后8-15,然后16-23、最后24-31位 。由于TCP/IP首部所有二进制整数均以大端方式传输,故称大端为网络字节序。大小端区别如下图:Endianess

IP分片(Fragmentation)

物理网络层一般要限制每次发送数据帧的最大长度,IP层发送数据报时,需要查询网络接口并获得MTU,如果IP数据报相对于MTU过大,则需要进行分片。分片可以发生在原始发送端主机上,也可以发生在中间路由器上(IPv6禁止中途分片,IPv4也应当尽量避免中途分片)。

把一份IP数据报分片以后,只有到达目的地才进行重新组装(Reassembly)。重新组装由目的端的IP层来完成,其目的是使分片和重新组装过程对运输层透明。

IP首部中包含的数据为分片和重新组装提供了足够的信息:

  1. 标识字段:每一个待发送的IP数据报均有唯一值,该值复制到每个分片中
  2. 用于标示“更多的片”的标志位,除最后一片,均置1
  3. 片偏移:该片偏移原始数据报开始处的位置
  4. 当数据报被分片后,每个片的总长度值要改为该片的长度值

当IP数据报被分片后,每一片都成为一个分组,具有自己的IP首部,并在选择路由时与其他分组独立。这样,当数据报的这些片到达目的端时有可能会失序,但是通过IP首部中的信息可以重新正确组装。

注意术语IP数据报(IP Datagram)、分组(Packet,简称包)的区别:

  1. IP数据报是指IP层端到端的传输单元(在分片之前和重新组装之后)
  2. 分组是指在IP层和链路层之间传送的数据单元。一个分组可以是一个完整的IP数据报,也可以是IP数据报的一个分片

IP层没有重传机制,这意味着,即使分片丢失一个,整个IP数据包就废了。L4协议必须明白这一点,并给出必要的处理机制。

IP路由选择

对于主机来说,路由通常很简单:

  1. 如果与目的主机直连(如PPP链路)或者在一个网络中(以太网、令牌环网),则直接把IP数据报发给目标主机
  2. 否则,把数据报发往一默认路由,由路由器转发该数据报

一般的说,IP层既可以配置成路由器的功能,也可以配置成主机的功能,几乎所有的Unix都可以配置成一个路由器。

IP可以从上层(TCP、UDP、ICMP、IGMP,即本地生成的)或者从一个网络接口(即待转发的)接收数据报。

IP层在内存中维持一个路由表,要发送每一份数据报时都要搜索该表一次。路由表中的每一项都包含下面这些信息:

  1. 目标IP地址:可以是主机的完整地址,或者网络地址,由标志字段确定。网络地址的主机号为0,表示目标是该网络(如以太网)中所有主机
  2. 下一站(跳)路由器(next-hop router,即直接相连的路由器)的IP地址,或者直接连接的接口IP地址
  3. 标志:一个标志用于标识是网络地址还是主机地址;另外一个标识下一跳路由器是真实路由器,还是直接连接的接口
  4. 为数据报传输指定的网络接口

IP路由是逐跳(hop-by-hop)进行的,IP并不知道到达目的地的完整路径,所有路由器只是为IP数据报提供下一个路由器的IP地址。

IP路由选择的步骤如下:

  1. 搜索路由表,寻找与目标IP地址完全匹配的条目(网络、主机号都匹配),如果找到,把报文发给下一站路由器或者直连接口
  2. 搜索路由表,寻找与目标网络号匹配的条目,如果找到,把报文发给下一站路由器或者直连接口。这种匹配方式必须考虑子网掩码
  3. 搜索路由表,寻找标记为默认(Default)的条目,如果找到,把报文发给下一站路由

如果以上步骤均不成功,则数据报无法发送,如果数据报来自本机,则应用程序可能获得一个“主机不可达”或“网络不可达”错误。

IP数据报具体处理过程如下:

  1. 如果数据报来自网络接口:
    1. 检查目的IP地址是否为本机任一IP地址或者广播地址。如果是,则根据首部协议字段,发送到指定协议模块处理,否则:
      1. 如果当前IP层被设置为路由器功能,则对数据报进行转发,否则
      2. 丢弃数据报

下面是路由的一个例子(bdsi主机期望发送IP数据报到Internet的192.48.96.9):

route-example

该例子的路由过程如下:

  1. bdsi搜索路由表,没有匹配目标地址主机号、网络号的条目,故使用默认路由,将其发送给主机sun。注意bsdi把链路层目的地址设为sun的MAC地址,该地址使用ARP协议获得
  2. sun接收到报文,发现其目标IP不是任何接口的IP地址,而且sun配置了路由功能,故进行转发。经搜索路由表,使用了默认条目,将其发送到下一站路由器netb,该路由地址为140.252.1.183,通过SLIP与sun连接。注意SLIP报文没有以太网报文那样的首部
  3. netb接收到报文后,执行了与sun类似的动作,使用默认路由将其转发给网关140.252.1.4
  4. 网关也使用自己的默认路由,该默认路由指定下一站路由为140.252.104.2

注意该例子里面的几个关键点:

  1. 所有主机和路由器都使用了默认路由。实际上,大多数主机和一些路由器可以用默认路由来处理任何目的,除非它在本地局域网上
  2. 数据报中的目的IP地址始终不发生任何变化。所有的路由选择决策都是基于这个目的IP地址
  3. 每个链路层可能具有不同的数据帧首部,而且链路层的目的地址(如果有的话)始终指的是下一站的链路层地址
子网寻址

所有现代主机都支持子网编址,不是把IP地址简单的分为网络号+主机号,而是进一步把主机号分解为子网号+主机号。这样做的原因是A、B类地址为主机号预留了过多的空间。

获取到IP网络号后,由系统管理员确定划分 多少bit给子网号、多少bit给主机号。例如对于B类网络地址140.252,如果剩下的16bit,8位分配给子网,8位分配给主机,则可以有254个子网、每个子网254台主机。

子网对外部路由器来说隐藏了内部网络组织(一个校园或公司内部)的细节,例如,对于网络140.252,外部路由器只需要一个路由表目,不管其内部划分多少个子网。子网对于内部路由器则不透明。

子网掩码

子网掩码是一个32bit的值,其中值为1的比特留给网络号和子网号,为0的比特留给主机号:

mask

给定IP地址、子网掩码后,主机可以确定IP数据报的目的地址的位置是哪里:

  1. 本子网上的主机
  2. 本网络中其他子网中的主机
  3. 其他网络上的主机
地址解析协议(ARP)

数据链路层,例如以太网、令牌环网,都有自己的寻址机制,上层协议必须遵从,这种寻址机制与IP无关。

对于以太网,当一台主机发送以太网数据帧给同一局域网中另外一台主机时,是使用48位以太网地址来确定目标网络接口的,设备驱动从来不会关注IP数据报中的目标IP地址。

ARP是链路层和网络层的地址转换桥梁,它提供了IP地址与数据链路层任何类型的地址之间的映射,因而根据IP数据报可以获取以太网数据帧需要的48位地址。ARP通常自动化、动态的完成IP与硬件地址的映射,通常用户、系统管理员关心。

举例:Linux下建立FTP连接的过程

ftp

 

该例子详解如下:

  1. FTP客户端调用gethostbyname函数,把主机名转换为IP地址。该函数载DNS系统中称为解析器,它要么使用DNS服务器,要么使用/etc/hosts静态列表
  2. FTP客户端使用获得的IP地址,创建TCP连接
  3. TCP发送连接请求分段到FTP服务器,即使用上述IP地址发送一个IP数据报
  4. 如果FTP服务器在本地网络(以太网、令牌环、PPP对端),则IP数据报可以直接送达。否则,通过IP选路函数确定下一站路由,并由其转发
    1. 假设FTP服务在本以太网内,那么发送端主机必须把32bit的IP地址转换为48Bbit以太网地址,这个转换就是ARP的功能
    2. ARP发送一份ARP请求的以太网数据帧,给以太网上每台主机,这是一种广播方式。该请求数据帧中包含了主机的IP地址,其意义是:如果你是该IP的主机,那么请回答你的硬件地址
    3. FTP服务器收到ARP广播报文后,发送包含硬件地址、IP地址的应答
    4. FTP客户端收到ARP应答,就可以进行IP数据报的传送了
    5. 发送IP数据报到FTP服务器

 在ARP后面有个基本概念,即网络接口具有唯一的硬件地址,在硬件层次上进行数据帧交换,必须有正确的硬件地址。TCP/IP协议栈虽然有自己的IP地址,但是知道IP地址并不能确定要发到那一台主机。

点对点链路不需要使用ARP,在建立链路时,必须告知内核,链路每一端的IP地址,像以太网那样的硬件地址并不涉及。

ARP高速缓存

每个主机上都有一个ARP高速缓存。这个高速缓存存放了最近IP地址到硬件地址之间的映射记录。高速缓存中每一项的生存时间一般为20分钟。 使用arp命令可以查询高速缓存。

ARP分组格式

ARP可以用于非以太网络,也可以用于解析非IP地址。ARP请求和应答分组的格式如下图所示:

arp其中:

  1. 以太网目的地址:如果为全1,则表示是广播地址。电缆上的所有以太网接口都要接收广播的数据帧
  2. 2字节以太网帧类型:表示后续的数据帧类型,对于ARP请求应答,该字段为0x0806
  3. 硬件类型:表示硬件地址的类型。1表示以太网地址
  4. 协议类型:要映射的协议地址类型。0x0800表示IP地址
  5. 硬件地址长度和协议地址长度分别指出硬件地址和协议地址的长度,以字节为单位
  6. op操作字段:指出四种操作类型, ARP请求1、ARP应答2、RARP请求3、RARP应答4
  7. 对于ARP请求来说,目的以太网地址为空
使用tcpdump命令检测网络上的ARP包
Shell
1
sudo tcpdump -en -i eth0

输出内容类似下面:

1
2
3
15:07:52.484316 48:5a:b6:9e:a6:79 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 60: Request who-has 192.168.1.254 tell 192.168.1.48, length 46
15:07:52.689430 d4:c9:ef:fe:78:c6 > 33:33:00:01:00:02, ethertype IPv6 (0x86dd), length 161: fe80::d6c9:efff:fefe:78c6.546 > ff02::1:2.547: dhcp6 solicit
15:07:54.332364 e0:06:e6:c7:c9:24 > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 342: 192.168.1.61.68 > 255.255.255.255.67: BOOTP/DHCP, Request from e0:06:e6:c7:c9:24, length 300

输出内容中,首先是时间戳,然后是源-目标以太网地址,然后是以太网帧类型,对于ARP,后续会包含其长度,以及who-has等信息。

ARP高速缓存超时设置

从伯克利系统演变而来的系统一般对完整的表项设置超时值为20分钟,而对不完整的表项设置超时值为3分钟。

管理员可以用arp命令把地址放入高速缓存中而不设置超时。

ARP代理

如果ARP请求发往另一个网络上的主机,那么连接这两个网络的路由器就可以回答该请求,这个过程称作ARP代理(Proxy ARP)。这样可以欺骗发起ARP请求的发送端,使它误以为路由器就是目的主机,路由器的功能相当于目的主机的代理。

ARP代理可以用在没有设置默认网关的网络中。

因特网控制报文协议(ICMP)

ICMP经常被认为是IP层的一个组成部分,用于传递差错报文以及其他信息。ICMP报文通常被IP层或更高层协议( TCP或UDP)使用。ICMP报文封装在IP报文内部:

icmp

ICMP报文的格式如下图所示:

icmp2

ICMP报文类型

ICMP报文的类型由8位类型字段、8位代码字段共同确定,如下表(最后两列说明该ICMP是查询还是差错报文):

icmp-types

 

区分查询报文、差错报文的原因是:差错报文需要特殊处理。例如,对于差错报文,永远不会再生成一个差错报文。

当发送一个ICMP差错报文时,产生ICMP报文的IP数据报的前8字节始终包含其中,这样就可以确定产生ICMP的协议、用户进程(根据端口号确定)。

以下场景不会产生ICMP差错报文:

  1. ICMP差错报文(但是ICMP查询报文可能产生)
  2. 目的地址是广播、多播地址的IP数据报
  3. 作为链路层广播的数据报
  4. 不是IP分片的第一片
  5. 源地址不是单个主机的数据报。即,源地址是零地址、环回地址、广播地址、多播地址的数据报不会产生ICMP差错报文
Ping程序

Ping程序的目的是为了测试另一台主机是否可达,该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答。通常来说,如果不能Ping通目标主机,那么就不能使用Telnet、FTP等高层协议。

大部分TCP/IP实现在内核中支持Ping服务器(不是用户进程)。

当返回Ping的ICMP应答时,会打印TTL、往返消耗时间。

在广域网中进行Ping,可能出现重复分组、失序分组(同一个序号出现多次,或者顺序乱掉)

Ping请求/应答报文格式

ping-msg

  1. 标识符:Unix实现中,通常将其设置为Ping客户端进程的ID
  2. 序号:从0开始,每发一次回显请求就增加1
记录路由(RR)选项

使用ping的-R选项可以提供记录路由功能。该选项导致ping程序发送的IP数据报(即ICMP报文)设置IP的RR选项,这样每个处理该IP数据报的路由器都把自己的IP地址放入数据报的选项字段中。

通过该方式记录路由,有一个缺陷:IP首部的空间有限,最多能存放9个IP地址。

IP数据报头部的RR选项格式如下:

ip-with-PP

  1. code:选项类型,对于RR,其为7
  2. len:选项的长度
  3. ptr:基于1的指针,指向下一个IP的位置
Traceroute程序

Traceroute只需要在目的端运行一个UDP模块不需要任何特殊的服务器应用程序。其原理是:当路由器收到一份IP数据报,如果其TTL字段是0或1,则路由器不转发该数据报,而是丢弃该数据报,并给信源机发一份ICMP“超时”信息,Traceroute程序检查该ICMP报文的IP头的源地址,即为路由器地址。具体步骤如下:

  1. 客户端发送TTL=1的IP数据报给目的主机,得到路由器1的地址
  2. 客户端发送TTL=2的IP数据报给目的主机,得到路由器2的地址
  3. ……重复以上过程,直到数据报能够到达目标主机

判断是否到达目标主机,依赖于最小TTL的判断,做法时,发送一个端口不可能(>65535)的UDP数据报给目的主机,让目的主机产生端口不可达错误的ICMP报文,从中判断TTL

输出分析
1
2
3
4
5
6
7
8
9
10
root@localhost:/# traceroute www.google.com
//目标主机名、IP地址,最大的TTL为30。40字节的数据包括20字节的IP头、8字节的UDP头
traceroute to www.google.com (173.194.117.48), 30 hops max, 60 byte packets
//对于每个TTL值,发送3个UDP数据报,每接收到一个ICMP报文,就打印往返时间
1  106.187.33.3 (106.187.33.3)  0.860 ms  0.840 ms  1.132 ms
2  124.215.199.173 (124.215.199.173)  1.261 ms  1.249 ms  1.235 ms
//三次走了不同的路由
3  otejbb206.int-gw.kddi.ne.jp (124.215.194.177)  1.813 ms otejbb206.int-gw.kddi.ne.jp (124.215.194.162)  32.599 ms otejbb205.int-gw.kddi.ne.jp (124.215.194.178)  1.827 ms
//如果在5秒种内仍未收到3份数据报的任意一份的响应,则打印一个星号,并发送下一份数据报
4  * * *
IP选路

IP层最重要的功能就是选路,IP层的简单处理流程如下图所示:

ip-process-flow

选路原理

route命令的说明请参考:Linux命令知识集锦

下面是一个时机的例子,注意以下几点:

  1. G用于区分是直接路由还是间接路由。
    1. 直接路由一般是指通过以太网直接访问(通过LAN路由器),不设置G标记,直接路由的底层报文分组中不单包含了IP地址,还包括目的地的链路层地址
    2. 间接路由的 IP地址指明的是最终的目的地,但是链路层地址指明的是网关(即下一站路由器)
  2. 关于子网掩码:子网掩码实际上就是路由条目使用的网络接口的子网掩码,由于内核知道每个条目对应的网络接口,因此可以推断出子网掩码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
root@localhost:~ route -ne
Kernel IP routing table
Destination     Gateway         Genmask          Flags   MSS Window  irtt Iface
#默认路由。每个主机都有一个或多个默认路由,如果在路由表中没有匹配条目则将数据报发往106.185.46.1
0.0.0.0         106.185.46.1    0.0.0.0          UG        0 0          0 eth0
#局域网路由
106.185.46.0    0.0.0.0         255.255.255.0    U         0 0          0 eth0
#环回路由
127.0.0.0       0.0.0.0         255.0.0.0        U         0 0          0 lo
#特殊路由:如果目的是主机(H)66.51.111.12,则路由器把IP数据报发给网关106.185.46.77
66.51.111.12    106.185.46.77  255.255.255.255  UGH       0 0          0 eth1
 
#下面是位于106.185.46.7的PPTP服务器连接了两个客户端后,自动生成的路由条目
#PPTP服务器
root@localhost:~# ifconfig
 
#以太网卡
eth0      Link encap:Ethernet  HWaddr f2:3c:91:50:b5:86
          inet addr:106.185.46.7  Bcast:106.185.46.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:29052 errors:0 dropped:0 overruns:0 frame:0
          TX packets:36072 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:25331473 (25.3 MB)  TX bytes:26714476 (26.7 MB)
#环回网卡
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:281 errors:0 dropped:0 overruns:0 frame:0
          TX packets:281 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:28249 (28.2 KB)  TX bytes:28249 (28.2 KB)
#对于每一个PPP连接,显示一个网络接口,P-t-P为对端的地址
ppp0      Link encap:Point-to-Point Protocol
          inet addr:192.168.10.1  P-t-P:192.168.10.100  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1396  Metric:1
          RX packets:13175 errors:0 dropped:0 overruns:0 frame:0
          TX packets:16974 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:3497419 (3.4 MB)  TX bytes:20714351 (20.7 MB)
 
ppp1      Link encap:Point-to-Point Protocol
          inet addr:192.168.10.1  P-t-P:192.168.10.101  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1496  Metric:1
          RX packets:25 errors:0 dropped:0 overruns:0 frame:0
          TX packets:19 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:1485 (1.4 KB)  TX bytes:1262 (1.2 KB)
 
root@localhost:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         106.185.46.1    0.0.0.0         UG    0      0        0 eth0
106.185.46.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0
#针对每一个PPTP对端主机的路由设置,均直连,通过对应的PPP接口
192.168.10.100  0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
192.168.10.101  0.0.0.0         255.255.255.255 UH    0      0        0 ppp1
 
#PPTP客户端
eth0      Link encap:Ethernet  HWaddr 00:0c:29:cc:9f:33  
          inet addr:192.168.1.111  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fecc:9f33/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:117 errors:0 dropped:0 overruns:0 frame:0
          TX packets:156 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:12406 (12.4 KB)  TX bytes:19847 (19.8 KB)
 
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:247 errors:0 dropped:0 overruns:0 frame:0
          TX packets:247 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:27029 (27.0 KB)  TX bytes:27029 (27.0 KB)
 
ppp0      Link encap:Point-to-Point Protocol  
          inet addr:192.168.10.101  P-t-P:192.168.10.1  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1400  Metric:1
          RX packets:19 errors:0 dropped:0 overruns:0 frame:0
          TX packets:25 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:1262 (1.2 KB)  TX bytes:1485 (1.4 KB)
 
alex@amethystine:~$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
#默认路由被修改,这导致了VPN的默认行为 ———— 所有连接通过VPN走
0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 ppp0
#通往PPTP服务器的主机路由,走网关192.168.1.1(一个路由器)
106.185.46.7    192.168.1.1     255.255.255.255 UGH   0      0        0 eth0
#局域网路由
192.168.1.0     0.0.0.0         255.255.255.0   U     1      0        0 eth0
#通往PPTP服务器的PPP链路,走ppp0直连
192.168.10.1    0.0.0.0         255.255.255.255 UH    0      0        0 ppp0

主机路由表的复杂性取决于主机所在网络的拓扑结构:

  1. 如果主机没有联网,TCP/IP协议仍然可以用于该主机。这种情况下的路由表只包含环回接口一项
  2. 如果主机连在一个局域网上,只能访问局域网上的主机。这时路由表包含两项:一项是环回接口,另一项是局域网(如以太网)
  3. 如果主机能够通过单个路由器访问其他网络(如Internet)。这时路由表增加一个默认表项指向该路由器
  4. 如果要新增其他的特定主机或网络路由。这需要额外的路由表配置
路由表的初始化

每当使用ifconfig初始化一个网络接口时,内核自动为接口创建一个直接路由:

  1. 对于PPP、Loopback,该直接路由的的目的是主机(H)
  2. 对于广播接口(例如以太网),该直接路由到达整个局域网

到达主机/网络的路由不是直连的,就必须加入路由表,其中一种方式是在系统引导时,手工添加route add命令。某些系统运行在特定的配置文件添加路由,例如/etc/defaultrouter

没有到达目的地的路由

如果最终没有找到匹配项,则:

  1. 如果IP数据报是当前主机产生的:给发出报文的应用程序报告“主机不可达差错”或者是“网络不可达差错”错误
  2. 如果IP数据报是转发的:给原始报文发送端发送ICMP主机不可达差错报文
让主机转发(Forward)数据报

一般主机不转发IP数据报,除非对它们进行特殊配置而作为路由器使用。 大多数伯克利派生出来的系统都有一个内核变量ipforwarding,其值控制是否转发IP数据报。不同系统的设置方式不同,有些系统默认允许转发IP数据报。

ICMP主机不可达差错

当路由器收到一份IP数据报但又不能转发时,就要发送一份ICMP“主机不可达”差错报文

ICMP重定向差错

当IP数据报应该被发送到其它路由器时,收到数据报的路由器就要发送ICMP重定向差错报文给IP数据报的发送端。 具体场景如下:

  1. 主机发送一份IP数据报给R1,通过默认路由
  2. R1收到数据报并且检查它的路由表,发现R2是发送该数据报的下一站。R1检测到R2在同一局域网上
  3. R1 发送一份ICMP重定向报文给主机,告诉它以后把数据报发送给R 2而不是R1

示意图如下:

ICMP-redirect

重定向一般用来让具有很少选路信息的主机逐渐建立更完善的路由表,允许TCP/IP主机在进行选路时不需要具备智能特性,而把所有的智能特性放在路由器端。

ICMP路由器发现报文

除了在配置文件中添加静态路由,还可以使用ICMP路由器发现报文初始化路由表。

一般认为,主机在引导以后要广播或多播传送一份路由器请求报文。一台或更多台路由 器响应一份路由器通告报文。另外,路由器定期地广播或多播传送它们的路由器通告报文, 允许每个正在监听的主机相应地更新它们的路由表。

用户数据报协议(UDP)

UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。

UDP封装格式如下:

UDP

UDP不提供可靠性:它把应用程序传给IP层的数据发送出去,但是并不保证它们能到达目的地。

应用程序必须关心IP数据报的长度。如果它超过网络的MTU,那么就要对IP数据报进行分片。如果需要,源端到目的端之间的每个网络都要进行分片。

UDP头部

UDPHead

  1. 源端口号、目的端口号:注意UDP端口号与TCP端口号是相互独立的,尽管某种知名服务如果同时提供TCP、UDP服务,会使用同一个端口号
  2. UDP长度字段:指的是UDP首部和UDP数据的字节长度,最少8
  3. UDP检验和:覆盖UDP首部和UDP数据。与TCP校验和不同,UDP校验和是可选的。如果接收端计算校验和失败,UDP数据报被悄悄丢掉

使用UDP很容易导致IP分片。如果分别发送1471、1472、1473、1474长度的4个UDP数据报,使用tcpdump可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
#前两份UDP数据报(第1行和第2行)能装入以太网数据帧,没有被分片
0.0                  host.1112  >  svr4.discard:  udp 1471  #UDP数据报的长度
21.008303 (21.0083)  host.1114  >  svr4.discard:  udp 1472  #UDP数据报的长度
#写1473字节的IP数据报长度为1501(1473+20+8),超过MTU=1500,需要分片
#IP数据报被分片后,会打印额外的信息
                                                       #frag后面的数字26304:IP头的标识字段
                                                       #:与@之间的数字1480:除去IP头的数据报长度,其中用户数据1472字节
                                                       #分片时,除了最后一片,除IP头的数据部分必须是8的倍数
                                                       #@后的数字是从数据报开始处计算的片偏移值
50.449704 (29.4414)  host.1116  >  svr4.discard:  udp 1473 (frag 26304:1480@0+)
50.450040 (0.0003)   host > svr4:  (frag 26304:1@1480) #第一份数据报的第二个分片,只有1字节的用户数据,偏移1480
75.328650 (24.8786)  host.1118  >  svr4.discard:  udp 1474 (frag 26313:148060+)
75.328982 (0.0003)   host > svr4:  (frag 26313:2@1480)

其中1473字节数据报分片情况如下:

ip-frag

ICMP不可达差错(需要分片)

当路由器收到一份需要分片的数据报,而在IP首部又设置了不分片(DF)的标志比特,也会产生ICMP不可达差错。
该差错可以被利用来测算网络路径中最小的MTU(路径MTU)。

最大UDP数据报长度

理论上,IP数据报的最大长度是65535字节,去除20字节的IP首部和8个字节的UDP首部, UDP数据报中用户数据的最长长度为65507字节。但是实际上大多数实现允许的值比理论值小,现在的大部分系统都默认提供了可读写大于8192字节的UDP数据报。

ICMP源站抑制差错

可以使用UDP产生ICMP“源站抑制(source quench)”差错。当一个系统(路由器或主机)接收数据报的速度比其处理速度快时,可能产生这个差错。注意是“可能”————即使一个系统已经没有缓存并丢弃数据报,也不要求它一定要发送源站抑制报文。

该报文格式如下:

ICMP-sourcequench

广播与多播

IP地址分为三类:单播(unicast)地址、广播(broadcast)地址和多播(multicast)地址。广播和多播仅应用于UDP。

所谓广播,是指主机要向网上的所有其他主机发送数据报;所谓组播,是指向属于多播组的多个主机发送数据报。

要理解广多播,需要首先理解主机对由信道传送过来帧的过滤过程:

frame-filter

  1. 网卡查看由信道传送过来的帧,确定是否接收该帧,若接收后就将它传往设备驱动程序——通常网卡只接收目的地址为网卡物理地址或广播地址的帧。另外多数接口均被设置为混合模式,这种模式能接收每个帧的一个复制,tcpdump就是以此为原理工作。目前,大部分网卡经过配置,都可以接收目的地址为多播地址或某些子网多播地址的帧。对于以太网,当地址中最高字节的最低位设置为1时表示该地址是一个多播地址
  2. 如果网卡收到一个帧,这个帧将被传送给设备驱动程序;如果帧检验和错,网卡将丢弃该帧
  3. 设备驱动程序将进行另外的帧过滤:
    1. 帧类型中必须指定要使用的协议(IP、ARP等等)
    2. 其次,进行多播过滤,检测该主机是否属于多播地址对应多播组
  4. 设备驱动程序随后将数据帧传送给下一层,对于IP数据报,就是传递给IP层。IP根据IP地址中的源地址和目的地址进行更多的过滤检测。如果正常,就将数据报传送给传输层(TCP/UDP)
  5. 每次UDP收到由IP传送来的数据报,进行过滤:
    1. 根据目的端口号,有时还有源端口号进行数据报过滤。如果当前没有进程使用该目的端口号,就丢弃该数据报并产生一个ICMP不可达报文
    2. 如果UDP数据报存在检验和错,将被丢弃

多播之于广播,好处是避免增加对广播数据不感兴趣主机的处理负荷,从上面的过滤过程看到,直到UDP层才能把广播数据丢弃。

使用多播,主机可加入一个或多个多播组。这样,网卡将知道该主机属于哪个多播组,然后仅接收主机所在多播组的那些多播帧。

广播

广播地址分为四种,本节一一描述。

受限的广播

受限的广播地址是255.255.255.255。该地址用于主机配置过程中IP数据报的目的地址,此时主机可能不知道所在网络的子网掩码,甚至IP都不知道。

在任何情况下,路由器都不转发目的地址为受限的广播地址的数据报,这样的数据报仅出现在本地网络中。

指向网络的广播

指向网络的广播地址是主机号为全1的地址。例如:A类网络广播地址为netid.255. 255.255,其中netid为A类网络的网络号。

路由器可以转发指向网络的广播。

指向子网的广播

指向子网的广播地址为主机号为全1且有特定子网号的地址。

作为子网直接广播地址的IP地址需要了解子网的掩码。例如,如果路由器收到发往128.1.2.255的数据报,当B类网络28.1的子网掩码为255.255.255.0时,该地址就是指向子网的广播地址;但如果该子网的掩码为255.255.254.0,该地址就不是指向子网的广播地址。

指向所有子网的广播

指向所有子网的广播也需要了解目的网络的子网掩码,以便与指向网络的广播地址区分开。指向所有子网的广播地址的子网号及主机号为全1。例如,如果目的子网掩码为255.255.255.0,那么IP地址128.1.255.255是一个指向所有子网的广播地址。然而,如果网络没有划分子网,这就是一个指向网络的广播。

多播

IP多播向多个目的地址传送数据,其主要应用场景包括交互式视频会议。

多播组地址

D类IP地址用于组播,请格式如下:

IP-D

与A、B、C三类地址不同,多播地址的28bit均用作多播组号,与主机号、子网号等概念无关。

用十进制表示,多播地址的范围是224.0.0.0 - 239.255.255.255。

能够接收发往一个特定多播组地址数据的主机集合称为主机组(host group)。其特征为:

  1. 一个主机组可跨越多个网络
  2. 主机组中成员可随时加入或离开主机组
  3. 主机组中对主机的数量没有限制
  4. 不属于某一主机组的主机可以向该组发送信息

某些多播地址被IANA确定为知名地址:

  1. 224.0.0.1:子网内的所有系统组
  2. 224.0.0.2:子网内的所有路由器组
  3. 224.0.1.1:用作网络时间协议(NTP)
  4. 224.0.0.9:用作RIP-2
多播组地址到以太网地址的转换

IANA拥有一个以太网地址块(高位24bit为00:00:5e):00:00:5e:00:00:00到00:00:5e:ff:ff:ff,其中1/2被分配为多播地址,并且限定多播地址首字节必须为01,所有,对应于IP多播的以太网地址范围是:01:00:5e:00:00:00到01:00:5e:7f:ff:ff。

这种地址分配将使以太网多播地址中的23bit与IP多播组号对应起来,通过将多播组号中的低位23bit映射到以太网地址中的低位23bit实现:

ip-mac-multicast-mapping

由于多播组号中的最高5 bit在映射过程中被忽略,因此每个以太网多播地址对应的多播组是不唯一的。设备驱动程序或IP层需要对数据报进行过滤。

单个物理网络的多播流程:

  1. 多播进程将目的IP地址指明为多播地址
  2. 设备驱动程序将它转换为相应的以太网地址,并发送出去
  3. 接收进程加入多播组——通知IP层,需要接收某个多播组的数据报
  4. 当一个主机收到多播数据报时向属于那个多播组的每个进程均传送一个副本

当把多播扩展到单个物理网络以外需要通过路由器转发多播数据时,情况比较复杂。需要使用IGMP(因特网组管理协议)来确定网络中属于确定多播组的任何一个主机。

Internet组管理协议(IGMP)

IGMP用于支持跨网络的多播,IGMP被认为是IP层的一部分,使用IP数据报传输。与其他报文不同IGMP有固定的报文长度,没有可选数据,IGMP报文通过IP首部中协议字段值为2来指明。IGMP报文的格式如下:

igmp-format

  1. IGMP类型:1表示多播路由器发出的查询报文;2表示主机发出的报告报文
  2. 组地址:查询报文中置0;报告报文中设置为需加入的组地址
IGMP协议
加入一个多播组

某个用户进程可以在主机的某个网络接口上加入多播组(这里暗示可以通过一个网络接口+一个多播地址唯一确定多播组),某个网络接口上的多播组的成员是动态变化的,进程可能加入或者退出。一个进程可以在多个网络接口上加入某个多播组。

IGMP报告和查询

多播路由器使用IGMP报文来记录与该路由器相连网络中组成员的变化情况,规则如下:

  1. 当第一个进程加入一个组时,主机就推送一个IGMP报告。如果多个进程同时加入同一多播组,则只发送一个报告
  2. 进程离开一个组时,主机不发送IGMP报告,即使是最后一个进程
  3. 如果所有进程都离开组,那么主机不再应答后续的IGMP查询报文
  4. 多播路由器定时发送IGMP查询来了解是否还有任何主机包含有属于多播组的进程
  5. 主机通过发送IGMP报告来响应一个IGMP查询
域名系统(DNS)

DNS是一种用于TCP/IP应用程序的分布式数据库,提供:

  1. 主机名与IP地址之间的转换
  2. 电子邮件选路信息

对应应用程序来说,对DNS的访问通过地址解析器(resolver)完成,解析器不是系统内核的一部分,在Unix系统中,解析器对应的库函数为gethostbyname()和gethostbyaddr(),前者根据主机名返回IP地址,后者反之。

DNS基础

DNS的名字空间和Unix的文件系统相似,呈现出一种树形结构:

dns

该名字空间具有以下特点:

  1. 根是一个未命名的节点
  2. 每个节点至多有63个字符
  3. 不区分大小写
  4. 任何一个节点的域名,就是把该节点与最高节点使用点号(.)连接起来

以点号结尾的域名称为绝对域名或完全限定的域名(FQDN,Full-qualified Domain Name),例如blog.gmem.cc.。任何不以点号结束的域名是不完全的。

顶级域被分为三部分:

  1. arpa:用作地址到名字转换的特殊域
  2. 7个3字符长的普通域
  3. 若干2字符长的普通域,均为国家代码(国家域、地理域)

一棵独立管理的DNS子树称为域(Zone),常见的例如gmem.cc这样的二级域。一旦接受授权机构的委派,来管理一个Zone,就必须为该Zone提供一台或者多台DNS 服务器。

一个DNS服务器至少管理一个Zone,反过来,一个Zone的管理者必须为其指定一个主DNS、一个辅助DNS。

如果像DNS发送一个查询请求,而该DNS的数据库中没有相应信息时,该DNS就同其它DNS服务器联系。一个DNS不一定知道域在哪个DNS,但是它必须知道如何与根服务器进行联系(知晓其IP)。

DNS一旦获取一条信息(IP-域名映射),则放入高速缓存,避免重复向其它DNS索取数据。

DNS报文格式

DNS请求/应答报文由12字节的首部,4个可变长字段组成:

dns-msg

  1. 标识字段由客户端设置,服务器返回之,用于匹配请求与应答
  2. 16bit的标志字段:dns-msg-header-flag
    1. QR:0表示查询,1表示响应
    2. opcode:通常值为0(标准查询),其他值为1(反向查询)和2(服务器状态请求)
    3. AA:表示“授权回答”——该名字服务器是授权于该域的
    4. TC:表示“可截断的”,使用U D P时,它表示当应答的总长度超过512字节时,只返回前512个字节
    5. RD:表示“期望递归”。这个标志告诉名字服务器必须处理这个查询,也称为一个递归查询。如果该位为0,且被请求的名字服务器没有一个授权回答,它就返回一个能解答该查询的其他名字服务器列表,这称为迭代查询
    6. RA:表示“可用递归”。如果名字服务器支持递归查询,则在响应中将该比特设置为1
    7. 随后3bit必须为0
    8. rcode:返回码字段。通常的值为0(没有差错)和3(名字差错)。名字差错只有从一个授权名字服务器上返回,它表示在查询中指定的域名不存在
  3. 问题数、资源记录数、授权资源记录数、额外资源记录数,这4个字段用于记录后续4个变长字段包含数据的个数
    1. 对于查询报文,问题数通常为1,其它为0
    2. 对于应答报文,回答数至少1,后续两项可以为0
  4. 查询问题,格式如下:dns-msg-question
    1. 查询名:需要查找的名字,它是一个或多个标识符的序列,每个名字以0结束。例如gemini.tuc.noao.edu的表示如下:dns-msg-question-name
    2. 查询类型:每个问题有一个查询类型,而每个响应(也称资源记录)也有一个类型。最常用的查询类型是A类型,表示期望获得查询名的IP地址。一个PTR查询则请求获得一个IP地址对应的域名。查询类型、类型如下表:dns-query-type
      1. A:即地址(Address)记录,用来指定主机名(域名)对应的IP地址记录
      2. AAAA:把主机名(域名)解析到IPv6地址
      3. MX:即邮件交换(Mail Exchange)记录
      4. CNAME:别名解析,可以把多个域名转到一个域名记录上
      5. URL:网址转发
      6. NS:指定域名由哪个DNS服务器来解析
      7. PTR:用于DNS反查,即根据IP地址来查域名
    3. 查询类:通常是1,指互联网地址
  5. 资源记录(仅响应报文):最后的三个字段,回答字段、授权字段和附加信息字段,均采用一种称为资源记录RR(Resource Record)的相同格式:rr
    1. 域名:RR中对应的名字,与查询名字段格式一致
    2. 类型:RR的类型代码,与查询类型值一致
    3. 类:通常是1,指互联网地址
    4. 生存时间:是客户程序保留该资源记录的秒数。RR通常的生存时间值为2天
    5. 资源数据长度:资源数据的数量。该数据的格式依赖于类型字段的值。对于类型1(A记录)资源数据是4字节的IP地址
DNS应答状态
返回消息 响应码
RCODE
说明
NOERROR 0

成功

也可能对应nodata响应,域名记录是存在的,但是对应请求的记录类型

FORMERR 1 DNS查询格式错误
SERVFAIL 2 服务器处理失败,没能完成请求处理
NXDOMAIN 3 域名不存在
NOTIMP 4 功能没有实现
REFUSED 5 服务器拒绝处理请求
YXDOMAIN 6 不应该存在的名字
XRRSET 7 RRset that should not exist, does exist
NOTAUTH 8 服务器不是Zone的权威
NOTZONE 9 名字不在Zone中
态主机配置协议(DHCP)

动态主机配置协议(Dynamic Host Configuration Protocol,DHCP),属于应用层协议,前身是BOOTP协议。它使用UDP协议工作,常用的2个端口:

  1. 67(DHCP server)
  2. 68(DHCP client)

DHCP通常被用于局域网环境,主要作用是集中的管理、分配IP地址,使Client动态的获得IP地址、Gateway地址、DNS服务器地址等信息,并能够提升地址的使用率。简单来说,DHCP就是一个不需要账号密码登录的、自动给内网机器分配IP地址等信息的协议。

报文结构

dhcp-msg主要字段说明:

  1. Xid:随机生成的一段字符串,两个数据包拥有相同的xid说明他们属于同一次会话
  2. Ciaddr:客户端会在发送请求时将自己的ip地址放在此处
  3. Yiaddr:服务器会将想要分配给客户端的ip地址放在此处
  4. Siaddr:一般来说是服务器的IP地址
  5. Chaddr:客户端的mac地址
  6. Giaddr:如果需要跨子网进行DHCP地址发放,则在此处填入经过的路由器的ip地址
  7. Sname:服务器主域名
  8. Options:可以自由添加的部分,用于存放客户端向服务器请求信息和服务器的应答信息
报文类型
报文类型 说明
DHCP DISCOVER

客户端开始DHCP过程发送的包,是DHCP协议的开始

由于客户端不知道DHCP服务器在哪,因此这个报文以广播的方式发送

 

DHCP OFFER 服务器接收到DHCP DISCOVER之后做出的响应,它包括了给予客户端的IP(yiaddr)、客户端的MAC地址、租约过期时间、服务器的识别符以及其他信息
DHCP REQUEST 客户端对于服务器发出的DHCP OFFER所做出的响应。在续约租期的时候同样会使用
DHCP ACK 服务器在接收到客户端发来的DHCP REQUEST之后发出的成功确认的报文。在建立连接的时候,客户端在接收到这个报文之后才会确认分配给它的IP和其他信息可以被允许使用
DHCP NAK DHCP ACK的相反的报文,表示服务器拒绝了客户端的请求
DHCP RELEASE 一般出现在客户端关机、下线等状况。这个报文将会使DHCP服务器释放发出此报文的客户端的IP地址
DHCP INFORM 客户端发出的向服务器请求一些信息的报文
DHCP DECLINE 当客户端发现服务器分配的IP地址无法使用(如IP地址冲突时),将发出此报文,通知服务器
交互流程

dhcp

 

当一台没有IP地址的主机接入到了网络中时,如果设置的时DHCP自动获取地址,就会向网络中发送DHCP请求获得IP地址,源IP为0.0.0.0,目的IP为255.255.255.255,源MAC地址正常,目的Mac地址为全F的广播包。

当客户端为 windows主机时,网卡配置为 DHCP获得地址时,就开始向网络中请求地址,先发送一个广播包,等待 1 秒之后,如果没有服务器应答,就发送第二个广播包,如果 9 秒后没有收到应答,则发送第三个广播包,等 13 秒,还没有应答,最后再发送一个包,等待16 秒后,最终在四个广播包没有应答的情况下,默认是放弃请求,为网卡自动配上一个私有 IP 地址,地址段为169.254.0.0/16,网络状态为“受限制或无连接”。

169.254.0.0/16为Link-local地址,不通过路由器转发,因此网关为0.0.0.0。该地址的生成逻辑:

  1. 需要将自已的IP和掩码网关都设为0,并随机生成一个IP,地址范围169.254.1.0到169.254.254.255,RFC3927中建议使用MAC来生成IP地址,这样可以使每个设备生成的IP都不一样,将设备同时探测同一个IP的可能性降到了最低
  2. 需要进行ARP探测,如果地址已经被占用,则重新选择

Link-local地址可以确保在DHCP不可用的情况下,设备不至于连接不上。

传输控制协议
TCP简介

与UDP不同,TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。在一个TCP连接中,仅有两方进行彼此通信,多播、广播不适用于TCP。

TCP通过下列方式来提供可靠性:

  1. 应用数据被分割成TCP认为最适合发送的数据块(而UDP中,用户产生的数据报的长度不变)。由TCP传递给IP的协议信息单位(PDU)称为报文段或段(segment)。MSS影响最大报文段的大小,相应的影响IP数据报的大小,它可用于确保避免分片
  2. 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果目的端不能及时收到一个确认,将重发这个报文段
  3. 当收到发自TCP连接另一端的数据,它将发送一个确认。由于确认报文很小,因此通常在数据分组中“捎带”发送,此捎带发送的方式由TCP实现的延迟确认算法决定,延迟确认算法会在一个特定的窗口时间(例如100-200ms)内,将确认报文放在缓冲区,以寻找能捎带之的分组,如果没有找到,则会单独发送确认报文
  4. TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错, TCP将丢弃这个报文段和不确认收到此报文段(等待发送端超时并重发)
  5. 由于IP数据报的到达可能会失序,因此其上的TCP报文段的到达也可能会失序。如果必要, TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层
  6. 由于IP数据报会发生重复, TCP的接收端必须丢弃重复的数据
  7. TCP可以提供流量控制:连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出

TCP提供“面向字节流的服务”:两个应用程序通过TCP连接交换8 bit字节构成的字节流,TCP不在字节流中插入记录标识符。如果发送端分三次分别传递30、40、10字节,接收端将不知道发送端是这样三次发送的,它可能以每次20字节的方式分4次接收。TCP对字节流的内容不进行任何解释。

TCP首部

TCP数据被封装在一个IP数据报中:

tcp-structure

其中TCP首部的格式如下:

tcp-head

  1. 源端口号、目的端口号:用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。有时一个IP地址与一个端口号被称为插口(Socket),插口对(Socket Pair)可以唯一确定每个TCP连接的双方
  2. 序号:用来标识从TCP发端向TCP收端发送的数据字节流。TCP使用序号来为传输的每个字节进行计数,到达2^32-1后从0重新计数。当新建一个连接时,SYN标志变为1。序号字段为主机为本次连接选择的ISN(初始序号),主机要发送的第一个字节序号标为ISN + 1(SYN占用了一个序号),发送一个段,其第一个数据字节是本次会话的第100字节时,则序号字段为ISN + 100
  3. 确认序号:应当是上次已成功收到数据字节序号加1(如果上一个段1-1024字节已经收到,下一个段包含字节序号2049-3072,接收端不能确认这一新报文段,而只能发回确认序号为1025的ACK;此外接收方不能对一个接收到的报文段进行否认,例如如果对收到的1025-2048报文段校验和失败,接收方能做的只是发回确认序号为1025的ACK)。只有ACK标志为1时,确认序号字段才有意义。注意,发送ACK无需额外代价,它与确认序号一样,是TCP头的组成部分,一旦TCP连接成功建立,ACK总是置1。TCP为应用层提供全双工服务,数据能在两个方向上独立地进行传输,因此,连接的每一端必须独立维护每个方向上的序号
  4. 首部长度:首部包含的32bit的个数,只有4位,因此首部最多60字节。如果报文段没有选项字段,则头部应该是20字节
  5. TCP首部包含6个比特位标记:
    1. URG 紧急指针( urgent pointer)有效
    2. ACK 确认序号有效
    3. PSH 接收方应该尽快将这个报文段交给应用层
    4. RST 重置连接
    5. SYN 同步序号,用来发起一个连接
    6. FIN 发端已经完成发送任务
  6. 窗口大小:连接的两端分别声明一个窗口大小,用于进行TCP的流量控制。最大65535字节
  7. 校验和:覆盖整个TCP报文段:TCP首部和TCP数据。由发端计算和存储,并由收端进行验证
  8. 紧急指针:URG为1时有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。用于发送端向另一端发送紧急数据
  9. 选项,包括以下常用字段:
    1. 最长报文段大小(MSS):每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志的那个段)中指明这个选项
  10. 数据:数据部分是可选的。到在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部
TCP性能影响因素
延迟确认

如本章前面所描述,“捎带”发送确认分组,可能带来性能问题。当出现双峰(bimodal)特征的通信时,需要数据分组用来捎带确认分组时,可能没有数据分组可用,从而导致窗口时间超时后单独发送确认分组。这种场景下,可以调整或者禁止延迟确认算法。

TCP慢启动

所谓慢启动,是指随着TCP连接使用时间的增长,传输速度会提高。慢启动可以防止因特网突然过载和拥堵,但是可能导致新连接的性能相对已经交换过一定数量的、“已调谐”的连接较差。

慢启动特征限制了TCP端点在任意时刻可以传输的分组数量,简单的说,发送端的每一个分组被接收到,它就获得额外传送两个分组的权限,当这两个分组被确认后,就获得四个分组,这种逐渐可以发送更多分组的方式称为“打开拥塞窗口”。

Nagle算法与TCP_NODELAY

每个TCP段至少包含40字节的头部,如果大量TCP报文只包含极少的数据,那么网络的性能就会严重下降。

Nagle算法试图在发送一个分组前,将大量的TCP数据绑定在一起,以提高网络效率。该算法鼓励使用全尺寸(MTU)的TCP段,只有所有其它分组都被确认后,Nagle才允许发送非全尺寸的分组。

Nagle与延迟确认算法存在交互问题:前者要求分组确认到达再发送分全尺寸数据;后者则可能导致确认延迟发送100-200ms。

通过设置TCP_NODELAY,可以禁用Nagle算法,但是应当保证向TCP写入大块数据,避免产生大量小的分组。

TIME_WAIT累积与端口耗尽

当TCP端点关闭连接后,内存中会维护一个小的控制块,时间为报文段最大生存期的两倍(2MSL,通常2分钟),用于确保该时间段内不会创建相同地址和端口号的连接,该机制用于防止新连接接收到旧连接的报文段。

由于现代高速路由器的应用,某些操作系统将2MSL设置为较小的值。

TCP连接的建立与终止

TCP是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须先在双方之间 建立一条连接。

连接的建立与终止

这里使用一个telnet命令的例子来说明TCP连接的建立和终止过程:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#命令和输出序列:使用telnet登陆服务,并立刻退出。
telnet remotehost service
Trying 140.252.13.35 ...
Connected to remotehost.
telnet> quit
Connection closed
 
tcpdump -S  #总是显示完整序号而不是偏移量
#tcpdump输出,包含7个报文段,这7个TCP报文段仅包含TCP首部。没有任何数据
#对于每一个TCP报文段,tcpdump输出格式为: 源 > 目的: 标志
#分组序号字段:1415531521,报文段中数据字节数为0。只有在:报文至少包含一个字节,或者SYN、FIN、RST之一置1时,分组序号显示在tcpdump结果中
#win 4096表示发送端通告的窗口大小;表示发送端的最大报文段长度选项(发送端将不接收超过这个长度的报文段)
1  0.0                  localhost.1037    > remotehost.service:  S  1415531521:1415531521(0) win 4096
#ack后为确认序号,只有在首部ACK字段被置1时显示,注意ack可以单独发送,也可以附在其它报文段上一起发送
2  0.002402 (0.0024)    remotehost.service > localhost.1037:     S  1823083521:1823083521(0) ack 1415531522 win 4096
3  0.007224 (0.0048)    localhost.1037    > remotehost.service:  .  ack 1823083522 win 4096
#上面是打开连接的3次握手
#下面是关闭连接的4次握手
4  4.155441 (4.1482)    localhost.1037    > remotehost.service:  F  1415531522:1415531522(0) ack 1823083522 win 4096
5  4.156747 (0.0013)    remotehost.service > localhost.1037:     .  ack 1415531523 win 4096
6  4.158144 (0.0014)    remotehost.service > localhost.1037:     F  1823083522:1823083522(0) ack 1415531523 win 4096
7  4.180662 (0.0225)    localhost.1037    > remotehost.service:  .  ack 1823083523 win 4096

在tcpdump输出中,使用一位字母来代表TCP首部的标志比特(TCP首部中的其他两个标志比特:ACK 、URG将作特殊显示):

flag-in-tcpdump

建立连接(三次握手,three-way handshake)

上面例子建立连接的过程分为三步:

  1. 请求端(Client)发送一个SYN段指明客户打算连接的服务器的端口以及初始序号(ISN)。对应序号为1的报文段
  2. 服务器发回包含服务器的初始序号的SYN报文段作为应答。同时,将确认序号设置为客户的ISN + 1以对客户的SYN报文段进行确认。注意SYN占用一个序号。对应序号为2的报文段
  3. 请求端将确认序号设置为服务器的ISN+1以对服务器的SYN报文段进行确认。对应序号为3的报文段

通过以上三个步骤——SYN、SYN+ACK、ACK,TCP连接正式建立,这称为三次握手。在这个例子里面,发送第一个S YN的一端将执行主动打开(Active Open),接收这个SYN并发回下一个SYN的另一端执行被动打开( Passive Open),双方都主动打开也是支持的。

终止连接(四次握手)

关闭连接需要四次握手——这是由于TCP连接具有半关闭(Half-close)的特性:TCP连接是全双工的(即数据在两个方向上能同时传递),因此必须在每个方向单独地进行关闭。当一方完成它的数据发送任务后就能发送一个FIN(通常由应用层执行)来终止这个方向连接,对端收到该FIN后,应当立即通知应用层。

收到一个FIN,只意味着对端到本端的数据流动结束,本端依旧可以向对端发送数据,但是现实应用中使用这种半关闭特性的场景是较少见的。

在上面的例子中,首先进行关闭的一方(即发送第一个FIN)将执行主动关闭,而另一方(收到这个FIN)执行被动关闭。双方都主动关闭也是支持的。

连接是由客户端发起的,通常,连接的关闭也应该由客户端发起。

连接建立的超时

如果无法连接到服务器,客户端会进行重试,tcpdump输出类似下面:

Shell
1
2
3
4
5
6
#尝试了3次连接,第二次间隔第一次为5.8秒,第三次间隔第二次为24秒
#大多数伯克利系统将建立一个新连接的最长时间限制为75秒
#tos为IP数据报的服务类型字段
1    0.0                 localhost.1024 >    remotehost.service:   S    291008001:291008001(0) win 4096  [tos 0x10]
2    5.814797 (5.8148)   localhost.1024 >    remotehost.service:    S    291008001:291008001(0) win 4096  [tos 0x10]
3    29.815436(24.0006)  localhost.1024 >    remotehost.service:    S    291008001:291008001(0) win 4096  [tos 0x10]
最大报文段长度(MSS)

MSS表示传往对端的最大数据块的长度,当一个连接建立时,连接的双方都要通告各自的MSS。如果一方不接受对方的MSS,则MSS置为默认值536。

一般来说,在没有分段发送的情况下,MSS越大越好。TCP模块发起/接受连接时,能自动把MSS设置为MTU-IP首部长度-TCP首部长度,对于以太网,结果是1460字节;对于IEEE 802.3,结果是1452字节。

TCP的半关闭

半关闭需要API提供支持,对于Unix,调用shutdown而不是close,并且传递第二个参数为1,则进行半关闭。所谓半关闭,是指可以关闭TCP的输入、输出通道中的一个;相对的完全关闭则是输入输出通道一起关闭。关闭输出通道总是很安全的,对端会在从缓冲区中读取所有数据后收到一个通知,表示流已经结束;关闭输入通道则比较危险,除非知道对端不会发送其它数据了。

半关闭的流程示例如下:

half-close

  1. 客户端发送FIN(调用shutdown),服务器确认
  2. 服务器进行发送数据给客户端,客户端读取并确认
  3. 服务器发送FIN(调用close),客户端确认,连接彻底关闭
TCP的状态变迁图

tcp-flow

查看该变迁图时需要注意:

  1. 粗的实线箭头表示正常的客户端状态变迁,用粗的虚线箭头表示正常的服务器状态变迁
  2. 两个导致进入ESTABLISHED状态的变迁对应打开一个连接;两个导致从ESTABLISHED状态离开的变迁对应关闭一个连接
  3. ESTABLISHED状态是连接双方能够进行双向数据传递的状态
  4. 左下角虚线框(主动关闭),包含4个状态
  5. 右下角虚线框(被动关闭),包含2个状态
  6. 图中11个状态的名称,与netstat命令显示的状态名一致
  7. CLOSED不是一个真实的状态,作为状态图假想的起终点
  8. 从LISTEN到SYN_SENT的变迁是正确的,但是某些TCP实现不支持
  9.  只有SYN_RCVD(图中SYN收到)是从LISTEN进入,而不是SYN_SENT进入,SYN_RCVD到LISTEN的状态变迁才是有效的。这意味着:执行被动关闭进入LISTEN,收到一个SYN、发送ACK+SYN进入SYN_RCVD,然后收到一个RST而不是ACK,便又回到LISTEN状态并等待另一个连接请求到来

下图显示了正常情况下,TCP连接的建立与终止过程中,客户与服务器所经历的不同状态:

normal-tcp-status-trans

2MSL等待状态

TIME_WAIT又称2MSL等待状态。

每个TCP实现必须选择一个报文段最大生存时间(Maximum Segment Lifetime,MSL)—— 是任何报文段被丢弃前在网络内的最长时间。此外,由于IP数据报具有TTL,因此在网络上存在是有限制的。后者是基于跳数,而不是定时器。MSL的值通常是 30秒、1分钟或者2分钟。

对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL,这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。

2MSL等待的结果是,2MSL期间定义连接的Socket(即:唯一标识Socket的四元组)不能再被使用。但是某些实现允许重用处于2MSL状态的端口(指定:SO_REUSEADDR)。

服务器通常是被动关闭,不会进入TIME_WAIT状态。

平静时间

对于来自某个连接的较早替身(相同端口对的上一个连接)的迟到报文段, 2MSL等待可防止将它解释成使用相同端口对的新连接的一部分。但是如果主机出现故障,则其会在MSL秒内重启,因而可能错误的接收替身报文段。

为了防止上述情况,RFC 793规定,TCP在启动后MSL秒内不接受任何连接,这段时间就是所谓的平静时间。

很少有实现遵守该规则。

FIN_WAIT_2状态

在FIN_WAIT_2状态,本端已经发送FIN,对端已经ACK。除非进行半关闭,否则对端的应用层已经意识到需要进行关闭,并向本端发送FIN来关闭另一方向的连接,只有对端完成这个关闭,本端才从FIN_WAIT_2进入TIME_WAIT状态。

这也意味着,本端可能一直处于FIN_WAIT_2,对端则一直处于CLOSE_WAIT,为了防止处于FIN_WAIT_2的无限等待,TCP实现中使用定时器进行处理。

复位报文段

TCP首部的RST比特用于“复位”,无论何时,只要向TCP连接发送的报文段出现错误,均会发出一个复位报文段。

到不存在的端口的连接请求

产生复位的一个常见原因是目标端口没有进程在监听。对于UDP,没有监听会导致一个ICMP端口不可达消息;对于TCP,则会产生复位(重置)。

下面是一个示例与响应的tcpdump输出:

Shell
1
2
3
4
5
6
#远程登陆一个没有使用的端口
telnet remotehost 20000
#tcpdump输出
1 0.0                 localhost.1087 > remotehost.20000: S 297416193:297416193(0) win 4096  [tos 0x10]
#复位,由于ACK在到达远程服务器的报文中没有置1,所以复位报文段中的序号被置为0
2 0.003771 (0.0038) remotehost.20000 > localhost.1087:   R 0:0(0) ack 297416194 win 0
异常终止一个连接

以FIN方式来终止连接是常规的方式,有时称为有序释放(Orderly Release)。

可以直接发送一个复位报文段,而不是FIN来中途释放一个连接有时称这为异常释放(abortive release)。需要注意的是RST报文段不会导致对端产生任何响应,对端收到RST的后将立即终止该连接,并通知应用层连接复位(一般提示:Connection reset by peer)

异常释放对于应用来说,有以下特点:

  1. 丢弃任何待发数据并立即发送复位报文段
  2. RST的接收方会区分另一端执行的是异常关闭还是正常关闭

Socket API通过SO_LINGER选项提供这种异常释放的能力。

检测半打开连接

如果本端已经正常/异常终止连接,而对端却不知道,这种连接叫做半打开的(Half-Open),任何一端主机出现故障(例如突然断电),均会导致半打开出现。只要不打算在半打开连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。

用TCP的keepalive选项能使TCP的一端发现另一端已经消失。

服务器立即ACK+RST

服务器可能在Ack客户端的SYN请求时立即RST,可能是服务器没有资源服务请求

报文错误

如果接收到无法处理的报文,也会立即发送RST,例如接收到错误的SLE、SRE字段后。

TCP选项

TCP首部可以包含选项部分。如下图所示:

tcp-options

  1. 每个选项的开始是1字节kind字段,说明选项的类型
  2. kind字段为0和1的选项仅占1个字节。其他的选项在kind字节后还有len字节。len用于表示选项的总长度,包括kind、len占用的2字节
TCP的超时、重传、坚持、保活

通过确认机制,TCP提供了可靠的传输层,但是数据和确认都有可能会丢失。TCP通过在发送时设置一个定时器来解决这种问题。如果当定时器超时时还没有收到确认,它就重传该数据。超时和重传的策略对于TCP实现很关键。

对每个连接,TCP管理4个不同的定时器:

  1. 重传定时器:确定多久对方没ACK,需要进行重传
  2. 坚持(Persist )定时器:使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
  3. 保活( Keepalive )定时器:检测到一个空闲连接的另一端何时崩溃或重启
  4. 2MSL定时器:测量一个连接处于TIME_WAIT状态的时间
超时与重传

下面是通过拔掉网线导致的超时重传的例子:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
localhost % telnet remotehost service
Trying 140.252.13.34...
Connected to remotehost.
Escape character is '^]'.
hello, world                          #正常发送本行
and hi                                #在发送本行前,拔掉网线
Connection closed by foreign host.    #9分钟后本端TCP放弃
#正常的TCP连接建立过程
1    0.0                       localhost.1029    >    remotehost.service:   S    1747921409:1747921409(0)  win 4096            
2    0.004811      (0.0048)    svx4.discard > localhost.1029:               S    3416685569:3416685569(0)  ack 1747921410 win 4096
3    0.006441      (0.0016)    localhost.1029    >    remotehost.service:   .    ack 1 win 4096        
#hello,world的传输与确认      
4    6.102290      (6.0958)    localhost.1029    >    remotehost.service:   P    1:15(14}    ack    1    win    4096
5    6.259410      (0.1571)    remotehost.service > localhost.1029:         .                ack    15   win    4096
#and hi的传输,以及12次重传过程                
6    24.480158     (18.2207)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
#注意重传时差,去整后分别是1、3、6、12、24、48和多个64秒,这种指数级的延迟模式称为:指数退避(exponential backoff)
7    25.493733     (1.0136)    localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
8    28.493795     (3.0001)    localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
9    34.493971     (6.0002)    localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
10   46.484427     (11.9905)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
11   70.485105     (24.0007)   localhost.1029    >    remotehost.service:  P    15:23(8)    ack    1    win    4096
12   118.486408    (48.0013)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
13   182.488164    (64.0018)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
14   246.489921    (64.0018)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
15   310.491678    (64.0018)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
16   374.493431    (64.0018)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
17   438.495196    (64.0018)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
18   502.486941    (63.9917)   localhost.1029    >    remotehost.service:   P    15:23(8)    ack    1    win    4096
#发送端TCP最终放弃并发送一个复位信号的过程
19   566.488478    (64.0015)   localhost*1029    >    remotehost.service:   R    23:23(0)    ack    1    win    4096

TCP超时与重传中最重要的部分就是对一个给定连接的往返时间(RTT)的测量。由于路由器和网络流量均会变化,因此我们认为这个时间可能经常会发生变化, TCP应该跟踪这些变化并相应地改变其超时时间。

当TCP超时并重传时,它不一定要重传同样的报文段。相反, TCP允许进行重新分组而发送一个较大的报文段,这将有助于提高性能(当然,这个较大的报文段不能够超过接收方声明的MSS)。在协议中这是允许的,因为TCP是使用字节序号而不是报文段序号来进行识别它所要发送的数据和进行确认。

坚持定时器

TCP通过让接收方指明希望从发送方接收的数据字节数(即窗口大小)来进行流量控制。如果窗口大小为0将有效地阻止发送方传送数据,直到窗口变为非0为止。

如果接收方修改窗口为非0的报文没有被发送方收到,那么发送方将一直等待。为了避免这种情况,发送方使用一个坚持定时器来周期性地向接收方查询,以便发现窗口是否已增大,这种查询报文称为窗口探查( Window probe)。

保活定时器

使用TCP可能遇到的一个神奇的情况是,没有任何数据流通过一个空闲的TCP连接,空闲时间可以是数小时、数天、数个星期或者数月,只要通信双方主机没有重启,则连接依然保持建立。

出现这种情况的原因是,应用层没有任何机制来探测非活动状态。尽管很多观点认为保活探测应该由应用层完成,但是这种探测可以由传输层本身进行,很多时候服务器希望知道客户主机是否崩溃并关机或者崩溃又重新启动。很多TCP实现提供的保活定时器可以提供这种能力(保活并不是TCP规范中的一部分)。

保活定时器的工作原理:

  1. 具有一个记时器,默认情况下2小时没有收到对方的封包,则开始发送探测报文
  2. 探测报文每75s发送一次,如果连续10次对方都没有回应,则认为对方出现故障,关闭TCP连接
  3. 每当收到对方的封包,记时器都会复位

Linux内核中和保活有关的参数:

Conf
1
2
3
4
5
6
# 记时器触发延迟
/proc/sys/net/ipv4/tcp_keepalive_time
# 保活封包发送间隔
/proc/sys/net/ipv4/tcp_keepalive_intvl
# 保活封包发送次数
/proc/sys/net/ipv4/tcp_keepalive_probes 
2MSL定时器

在TCP连接终止期间使用。参考2MSL等待状态。

传输层安全协定(TLS)

TLS是一个包装其它协议(例如HTTP)的协议,它在确保通信双方身份的前提下,保证数据被安全、完整的传输。在身份认证上,TLS依赖于CA发布的数字证书。

简单网络管理协议(SNMP)

参考:SNMP协议学习笔记

超文本传送(转移)协议(HTTP)

参考:HTTP协议学习笔记

实时通信协议族

参考:实时通信协议族

← Maven依赖速查表
Linux运行级别和启动顺序 →

针对该文章的评论功能已关闭

Related Posts

  • 类UNIX系统下使用Dnsmasq
  • 通过ExternalDNS集成外部DNS服务
  • 记录一次KeyDB缓慢的定位过程
  • Kubernetes上和DNS相关的问题
  • WebSocket协议

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2