从TCP/IP协议栈的角度看,网络层负责两点之间跨网络(跨局域网)的通信,可以将数据进行跨网络路由传输。从其与上层传输层的协作关系来看,传输层(的TCP协议)负责提供数据可靠地跨网络传输的策略,而网络层(的IP协议)提供将数据跨网络传输的能力,TCP和IP相互配合,就具有了将数据可靠地跨网络传输到对方的能力。
本文主要针对IP协议进行讨论,并对一些周边技术进行详细或简单的讨论。IP协议的功能大致分为三个部分:IP寻址、路由和数据的分片/组装。
协议段格式
- 版本(Version) 标识IP首部的版本号,IPv4的版本号即为
4
。不考虑中间的实验版本,IPv4的下一个版本为IPv6。 - 首部长度(IHL) 标识IP报头的长度。基本单位为 4 Bytes,最大能表示的报头大小为 60 Bytes。IP报文的首部包含两部分:20 Bytes 的报头和可选项。
- 服务类型(Type Of Service) 标识报文的转发依据,可以指定为最低延迟、最大吞吐、最大可靠性等值。
- 总长度(Total Length) 标识报文的总大小,单位是字节,最大能表示 65535 Bytes。
- 16位标识(Indentification)、3位标志(Flags)、13位片偏移(FO) 与IP报文的分片和组装有关,下文再谈。
- 生存时间(TTL) 指定报文到达目的地的最大跳数,每经过一次路由,该值减1,直至减为0,丢弃这个包。可以避免网络中存在大量游离报文。
- 协议(Protocol) 决定IP报文解包时,将数据交付给上层的哪个协议。
- 首部校验和(Header Checksum) 检查报文内容是否正确,如果出错则直接丢包。
- 源IP地址(Source Address) 和 目的IP地址(Destination Address) 分别记录发送方和接收方的IP地址。
- 选项(Options) 长度可变,一般在测试和网络诊断时使用。
网段划分
现实的网络体系是由无数个局域网构成的,所谓的跨网络通信,即是跨局域网通信。一台机器a要将数据跨网络传输到机器b,在这个过程中必然要在网络中寻找主机b,寻找目标主机是根据目标IP进行的,为了提高查找效率,可以先找到主机b所在的局域网,然后通过局域网通信将数据最终送达目标主机。
与上述查找逻辑相符,IP地址分为两部分:网络号和主机号。网络号用于标识网络中的某个局域网,而主机号用于标识网络中的某一台主机。在同一网段内,所有主机的网络号相同,但是必须拥有不同的主机号;两个不同的网段,网络号一定不同,但是主机号可以相同。在路由过程中,首先根据目标IP的网络号找到目标主机所在的局域网,再最终将数据包送达至目标主机。这即IP寻址的大致流程。上述的局域网是个宽泛的概念,在这里泛指公网下的所有子网。
在IPv4中,IP地址的大小为 32 bit,通过合理划分网络号和主机号,就可以保证在相连的子网中,每台机器的IP地址都不相同。所谓的网段划分,讨论的即是网络号和主机号的划分。现代网络中主流两种网段划分方式:分类划分和CIDR划分。
分类划分
在分类划分方式中,IP地址被划分为A、B、C、D、E五类,各类的网络号划分情况如下:
- 对于A类划分,第一位固定为0,前8位为网络号,可分配的IP地址为
0.0.0.0
到127.255.255.255
; - 对于B类划分,前两位固定为10,前16位为网络号,可分配的IP地址为
128.0.0.0
到191.255.255.255
; - 对于C类划分,前三位固定为110,前24位为网络号,可分配的IP地址为
192.0.0.0
到223.255.255.255
; - 对于D类划分,可分配的IP地址为
224.0.0.0
到239.255.255.255
; - 对于E类划分,可分配的IP地址为
240.0.0.0
到247.255.255.255
。
在分类划分中,每类IP的网络号和主机号范围都被定死,缺乏灵活性,造成IP地址被大量浪费的情况。例如,在B类划分中,理论上可以划分出 16384 个子网,每个子网中最多可以有 65534 台主机,但实际上不会存在一个子网中存在这么多主机的情况,因此大量的IP地址被浪费了。针对这种情况,提出了新的划分方式: CIDR(Classless Interdomain Routing)划分。
CIDR划分
在CIDR划分中,引入了子网掩码(Subnet Mask)来区分网络号和主机号。子网掩码是一个32位无符号整型,通常以一串数字‘0’结尾。将IP地址与子网掩码进行按位与运算,即得到这个IP地址的网络号。
子网掩码还有一种更简洁的表示方式,例如140.252.20.68/24
,表示对于IP地址140.252.20.68,其子网掩码的高24位为1,即子网掩码为255.255.255.0,这个IP地址的网络号为140.252.20.0。
相比分类划分,CIDR的划分方式更加灵活,减少了IP地址的浪费。在现行方案中,使用分类划分与CIDR相结合的方式,尽量减少了IP地址的浪费。
私有地址、公网地址和特殊地址
在IPv4中,理论上最多可以分配42亿多个IP地址,在互联网快速发展的今天,入网设备大量增加,给全世界每一个入网设备都各自分配一个IP地址是不现实的,即IP地址资源逐渐稀缺。考虑到这一点,现代网络体系中,将互联网人为划分为了两大部分:公网和私网,每个运行在公网上的机器,都拥有一个公网IP,公网IP在世界范围内唯一的标识了一台主机;私网运行在公网之下,不为其分配公网IP,取而代之,每台在私网中的机器都拥有一个对应的私网IP。即:IP地址被硬性地划分为公网IP和私网IP。如此一来,只需要给公网中的机器分配不重复的IP地址即可,降低了IP地址的分配压力。在不同的私网中,机器的私网IP可以相同。至于如何解决私网中的主机和公网中的主机如何通信的问题,会在下文进行讨论。
如果一个组织要在内部搭建局域网,只将IP地址用于局域网内的通信,而不直接连到公网上,理论上任何IP地址都可以使用,但是RFC 1918
规定了用于组建局域网的私有IP地址应为以下几类:
10.*
前8位是网络号,共16,777,216个地址172.16.*
到172.31.*
前12位是网络号,共1,048,576个地址192.168.*.*
前16位网络号,共65,536个地址
此外,还有几种特殊的IP地址:
- 如果地址中的主机号全为0,则为网络号,代表所在的局域网;
- 如果地址中的主机号全为1,则为广播地址,用于给所在子网中的所有相连主机发送数据;
- 形如
127.*
的地址,为本地环回(Loop Back)地址,通常是127.0.0.1。当以本地环回地址为目标地址时,数据包通过本地的网络协议栈发给主机自身,常用于测试。
路由概述
在组网层面,不同子网的划分是通过路由器进行的。路由器本质也是某个子网中的一台主机,也要配置IP地址。路由器一般是子网中的第一台设备,ip地址一般为网络号.1
。路由器可以看作为一个子网的"门户",并作为子网内所有主机的管理者,对子网中的主机的IP地址进行管理。同一个路由器管理的子网中,其中设备的主机号不能相同,即私有IP不能相同,当子网中的设备不断变化时,手动保证这一点是比较困难的。一般的路由器都具有DHCP(Dynamic Host Configuration Protocol)功能,可以自动给子网中的新增主机分配IP地址,避免了手动管理IP的不便,所以路由器也是一个DHCP服务器。
数据是通过路由器进行跨网络传输的,路由器作为一个"中转",至少要连接两个子网,即同时位于两个子网中,所以路由器要配置多个IP地址。当一个数据包到达路由器时,首先通过数据包的目标IP地址和其子网掩码进行计算,得到其目标主机所在网络的网络号,拿这个目标网络号与所有相连网络的网络号逐个进行比较(查路由表),如果一致,则将报文发送给对应的路由器,否则继续向下查表。查路由表的结果大致有以下三种可能:
- 在路由表中找到了目标网络,给出具体的下一跳
- 在路由表中未找到目标网络,转入默认路由,一般是同一网段中的出口路由器(不考虑含多个路由器的情况)
- 当前路由器就是目标主机的入口路由器,将数据转发给目标主机。
正常情况下,数据包在网络中不断被路由,直至到达目标主机所在网络,并最终送达至目标主机。至于路由器在路由过程中的路径选择问题,在这里不做过多讨论。
数据链路层概述和MTU
为了方便对下文中某些内容的叙述,这里穿插对网络层的下一层——数据链路层进行讨论。
在网络原理概述和Socket中,对同一个局域网中的通信过程做了大致说明,数据链路层解决的即是在同一个子网中,直接相连的主机之间的数据交付问题。数据链路层中的MAC帧格式如下:
- 6位目的地址和源6位地址 记录通信的目的MAC地址和源MAC地址,MAC地址长度为48位,是在网卡出厂时设定不变的;
- 2位协议类型 标识向上交付给哪一个协议,有三个值,分别为IP、ARP和RARP;
- 4位FCS校验 位于帧末尾,用于检查数据是否出错。
在网络原理概述和Socket中,同样提到了局域网通信中的数据碰撞问题,可以通过交换机划分碰撞域,降低碰撞发生的概率和影响范围。此外,链路层的报文越长,报文停留在局域网中的时间越长,发生碰撞的概率就会越大,所以每次要尽量发送短小的报文,规定:链路层的数据部分不能超过MTU(Maximum Transmission Unit)。MTU的大小通常是由网卡设置的,在不同的环境和配置中可能不同,在以太网中,MTU的默认值一般是 1500 Bytes。
# 使用ifconfig查看ip地址、MAC地址和MTU
[shr@ Tue Nov 12 16:07:29 ~ $] ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.25.37.207 netmask 255.255.192.0 broadcast 172.25.63.255
inet6 fe80::216:3eff:fe37:adc2 prefixlen 64 scopeid 0x20<link>
ether 00:16:3e:37:ad:c2 txqueuelen 1000 (Ethernet)
RX packets 8112196 bytes 2024567414 (2.0 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5420686 bytes 725825234 (725.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
数据链路层的数据部分是上层数据经过一步步封装报头得到的,为了使链路层的数据大小满足规定,MTU还会继续向上约束网络层和传输层,到达传输层时,限制传输层单次交付数据部分的大小不能超过MSS(Maximum Segment Size),MSS的值大致为1460 Bytes。
IP数据包的分片和组装
承上,数据链路层不能一次发送过大的报文,所以要求上层不能一次交付过大的报文。在IP层,当上层交付给自己的报文太大时,发送方所在的路由器会对IP报文进行分片,接收方收到零散的报文时,会在网络层对IP报文进行重新组装。如果上层使用的是TCP协议,双方会在通信过程进行MSS协商,并且TCP有自己的滑动窗口和流量控制机制,所以可以控制每次从缓冲区取出发送的数据大小小于MSS,故而上层使用TCP协议时,不需要在网络层进行数据包分片。而UDP报文的最大长度为64KB,所以对IP数据包的分片主要针对的是上层的UDP等报文。
在IP的协议段格式中,第5到8个字节是与IP数据包的分片有关的:
- 16位标识(Indentification) 标识数据片的归属。相同的标识,同属于一个完整的IP报文。
- 3位标志(Flags) 结束标识,标识数据片是否为IP报文的末尾数据片。为 0 则表示是末尾数据片,否则为中间数据片。为了方便叙述,下文统一称这个字段为结束标记字段。
- 13位片偏移(FO) 标识数据片在完整IP报文中的偏移量。片偏移以 8 Bytes 为基本单位,最大能表示的片偏移大小为 65535。
每个数据片都是一个IP报文,都要有完整的IP报头。站在接收方角度,接收到的都是正常的IP报文。接收方可以根据报文的结束标记和片偏移判断报文是否分片:如果片偏移为0,结束标记为0,则会说明报文没有分片。相同的标识,同属于一个完整报文,接收方会根据标识字段保证同属于一个报文的数据片最终会被组装到一起。
在网络传输过程中,每个数据片都有可能丢包。发生丢包时会有以下几种可能:
- 第一个数据片丢包 缺少片偏移为0的数据片
- 中间的数据片丢包 中间的序号不连续。可以将一个数据片的起始序号看作其片偏移,结束序号看作为片偏移+数据部分大小。接收方会根据序号进行排序,以保证报文被组装的正确性。
- 最后一个数据片丢包 缺少结束标记为 0 的数据片。
无论发生哪种丢包,接收方都能通过相关字段识别到。如果有任意一个数据片发生丢包,则整个报文都要重发,站在完整的IP报文角度,会增大IP报文的丢包概率,所以不建议分片。
在IP协议字段中,片偏移字段是以8字节为基本单位的,在分片时为了更好描述分片偏移,一般都会要求每个分片(除了最后一个)的大小都是8的整数倍大小。
IP协议相关技术
ARP和RARP
当数据包通过路由到达目标主机所在的网络之后,目标网络中的路由器需要将数据包转发给同一网络中的目标主机。同一个网络内的通信是以MAC地址作为通信地址的,路由器在链路层准备进行数据发送时,需要填充自己的MAC地址和目标主机的MAC地址,此时路由器拥有目标主机的IP地址,但并不一定知道目标主机对应的MAC地址。ARP(Address Resolution Protocol)协议用于解决这一问题,在局域网中,可以通过ARP协议以一台主机的IP地址获取其对应的MAC地址。
ARP报文的长度为 28 Bytes,其格式如下:
在这里主要关注最后五个字段:
- 2位操作(OP) 有两个可选值,为1时表示ARP请求,为2时表示ARP响应
- 发送端以太网地址和发送端IP地址 记录发送端的地址信息,一般是路由器
- 目的以太网地址和目的IP地址 记录目标主机的相关地址信息。
在获取目标主机的MAC地址的过程中,路由器发送ARP请求的流程为:将OP字段置为 1,在发送端对应的字段填入自己的MAC地址和IP地址,在目的主机对应的字段填入目标主机的IP地址,向当前网络中的所有主机广播这个ARP请求。当网络内的主机收到ARP报文时,首先会判断OP字段的值,这决定了接下来主机要关心ARP报文的那一部分。当收到请求报文时,主机关注最后两个字段中的信息,判断目的IP与自己的IP是否相同,如果不同,则丢弃这个ARP报文,如果相同,则组织ARP响应,发送给ARP请求中对应的主机(ARP请求中包含了路由器的MAC地址)。
路由器接收ARP响应的流程为:路由器收到ARP报文,通过OP字段判断出这是一个ARP响应报文,然后关注报文中记录的发送端相关的信息,将发送端的MAC地址和IP地址建立映射关系,从而得到目标主机的MAC地址。
ARP不是每次都要进行的,当获取ARP响应后,主机会暂时保存IP地址和MAC地址的映射信息,即ARP缓存。当ARP缓存失效时,主机才会进行ARP请求。在实际情况中,主机可以通过自己的IP地址和子网掩码进行计算得到网络号,并对网络中的所有主机分别发送ARP请求,得到一批ARP响应,并进行缓存,如此一来可以避免在后续的一段时间内多次进行ARP请求。ARP缓存会失效,这是因为当网络中的设备发生移动、更换或关闭时,主机被分配的IP地址可能发生变化,原先的IP地址与MAC地址的对应关系就会发生变化。在网络传输过程中,ARP请求随时可能会进行。当多次收到发送端IP相同的ARP应答时,以最新的ARP包为准。
RARP(Reverse Address Resolution Protocol)的作用与ARP相逆,通过主机的MAC地址获取IP地址。大部分设备都可以通过设置或者DHCP获取自己的IP地址,但也存在一些嵌入式设备无法通过外部方式得到自己IP地址的情况,此时可以通过RARP解决问题。
ARP和RARP协议位于MAC帧协议之上,归属到数据链路层。
DNS概述
每次访问某一台主机,都要以这台主机的IP地址对其定位,对于普通用户来讲,记忆一串数字型的字符串往往成本较高,在早期,人们建立了"主机名-IP地址"
的映射,以便于访问某台主机。主机名是一个自定义的字符串,主机名-主机IP的映射在一个hosts文件被保存与维护,起初由互联网信息中心整体管理一份hosts文件。在这个体系下,如果新增一台主机或者原先的主机名-IP地址映射发生变化,中心的hosts的文件就得更新,其他计算机就不得不定期下载最新的hosts文件。同时,hosts文件中的主机名不能重复。随着入网设备不断增加,这种hosts文件的体系的可行性逐渐减低。取而代之,在现代网络中使用DNS(Domain Name System)来管理所有主机名(域名)-IP地址的映射。
# 查看当前机器中的hosts文件 -> 已过时
[shr@ Mon Nov 11 19:58:26 ~ $] cat /etc/hosts
127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.1.1 Aliyun
172.16.57.230 iZbp163kkbkx5oblljdsp5Z iZbp163kkbkx5oblljdsp5Z
172.25.37.207 iZ2zed7lj4oethzhepuzv3Z iZ2zed7lj4oethzhepuzv3Z
在DNS中,使用域名系统遵循某种规律赋以一台主机不重复的字符串名称,将其作为主机名。域名由几个有意义的英文字母或字符串用点号(.)连接而成,域名具有层次性。
域名由域名服务器进行管理,域名服务器也具有层次性,可以管理其所在层(ZONE)的域名信息。
域名服务器的分层结构类似于一棵多叉树,位于根部的叫做根域名服务器,对于DNS的检索至关重要。各个分层都设有各自的域名服务器,各层的域名服务器都了解下一层所有域名服务器的IP地址。每个域名服务器都了解根域名服务器的IP地址。如果从根域名服务器开始检索,可以访问到所有域名服务器中的信息。
当在浏览器中输入一个域名进行访问时,浏览器(DNS解析器)会进行DNS查询:
- 先在自己所在域的域名服务器中进行检索,如果找到了域名对应的IP地址就直接返回;
- 如果没找到,就层层向上在各个域名服务器中进行检索,在这个过程中找到就返回;
- 直至到达根域名服务器,开始从根域名服务器向下遍历域名服务器进行检索。
- 在从根服务器向下遍历的过程中,实际上会根据域名的层次结构进行查询优化,类似于字典树的查询过程。
当获取主机名对应的IP地址后,浏览器根据IP地址访问目标主机。
ICMP概述
在一个新搭建好的网络中,往往会测试网络是否畅通,IP协议不保证可靠性,无法告知上层是否丢包以及丢包原因,而ICMP可以完成这个工作。ICMP位于IP协议之上,传输层之下,基于IP协议工作,归于网络层协议,有两个主要功能:
- 确认IP数据包是否成功到达目标地址;
- 通知发送过程中IP数据包被丢包的原因。
使用ICMP,目标主机和目标网络中的路由器都可以对ICMP报文进行应答,假使目标主机处于关机状态而导致报文不可达,路由器也会返回对应的丢包原因。
在使用ping进行网络诊断时,本质是使用原始套接字和ICMP,绕过传输层直接在网络层发送ICMP报文测试目标主机是否可达。本质是给目标主机发送一个ICMP Echo Request,如果目标主机可达,则会返回一个ICMP Echo Reply。
ICMP只适配IPv4,当基于IPv6时,需要使用ICMPv6。
NAT和NAPT
在上文中说道,互联网被人为的划分为公网和私网,目的在于缓解IP地址不足的问题:只需要给公网中的每一台主机分配唯一的IP地址即可,不同子网中的主机,私网IP可以相同。但是也遗留了一个问题:私网中的主机如何与公网中的主机进行通信?由此引出NAT(Network Address Translation)和NAPT(Network Address&Port Translation)。
当一台局域网中的主机以一台公网主机为目标进行通信时,数据进行跨网络传输,会经由路由器进行报文转发,路由器首次收到这个报文时,会建立一张NAT转换表,存储"内网IP-路由器IP"
的映射,并将报文中的源IP替换为自己的IP地址,继续向外路由。当公网主机收到报文并向内网发送应答报文时,路由器继而收到路由而来的报文,根据NAT映射表中的左侧内容,将报文交付给内网主机。假设路由器接入了公网,并且没有经过其他路由,站在公网主机视角,自己是跟路由器直接进行通信的。
如果局域网内,同时有多台主机向同一台公网主机发送报文,路由器为了对主机进行区分,会对自己的IP地址和端口号通信同时进行映射,即在NAPT转换表中建立"内网IP-路由器IP:端口号"
的映射,不同的内网主机对应不同的端口号。
在NAT和NAPT转换表中,左侧的字段在同一个局域网中是唯一的,右侧的字段在对外的网络中也是唯一的,NAT和NAPT转换表中记录的是双向映射,支持双向转换。
NAT和NAPT都依赖于自己的转换表,在转换表中建立映射的前提是,局域网主机必须首先向外发送数据,而无法从外部直接与内部主机建立连接。当NAT和NAPT出现问题,导致转换表失效时,所有连接都需要被重置。
NAT和NAPT是路由器的重要功能,工作在网络层。NAT和NAPT是当下解决IPv4地址资源逐渐枯竭问题的有效方式。
内网穿透概述
通过某种手段,实现公网主机向内网主机发送消息,即内网穿透。FRP(Fast Reverse Proxy)是实现内网穿透的一种常用方式,可以将内网主机暴露在公网。
要通过FRP实现内网穿透,首先需要一台部署在公网中的服务器,然后在内网的主机下载FRP客户端,在服务器下载FRP服务端,当FRP客户端启动时,会首先向服务器发送信息并建立连接,并注册自己想要暴露的应用,FRP客户端和FRP服务端会保持这个连接,用于FRP服务端获取客户端状态,以及进行请求与响应转发。当外部设备访问内网主机时,会向FRP服务端发送请求,由服务端将请求转发给内网主机;当内网主机返回响应时,也会由服务端将响应转发给外部设备。
在一个内网中,可以有多个FRP客户端访问同一个FRP服务端。FRP面向的是内网中的服务器,本质是一种反向代理。
代理概述
代理是一种应用软件,通常被部署在应用层,用于网络请求和响应的转发。代理位于服务端和客户端之间,根据面向对象的不同,代理可以分为两类:正向代理(Forward Proxy)和反向代理(Reverse Proxy)。
正向代理面向客户端,接收客户端的请求转发给服务端,并将位于外部网络的服务端的响应转发给客户端。正向代理的典型用途是为局域网中的主机提供访问外部网络的途径。注意,NAT和NAPT不是代理,NAT和NAPT工作在网络层,而代理是一种具体的应用软件。正向代理也可用于突破某些网络的访问限制。
反向代理面向服务器,即客户端通过反向代理访问位于内部网络中的服务器。反向代理的典型用途是将位于防火墙后面的服务端暴露在网络中供客户端访问。反向代理可用于负载均衡,客户端发送请求给代理服务器,代理服务器根据内网中多台服务器的负载情况对请求进行转发,提高系统的可靠性。多台服务器在外暴露为一台服务器。