【网络】IP 网络层和数据链路层
IP 协议详解
1. 概念
1.1 四层模型
- 应用层:解决如何传输数据(依照什么格式 / 协议处理数据)的问题
- 传输层:解决可靠性问题
- 网络层:数据往哪里传,怎么找到目标主机
- 数据链路层(物理层):数据在物理硬件层面上传输
网络层的 IP 协议,就是让网络,拥有将数据从 A 主机发送到 B 主机的能力。
注意:拥有此能力,并不代表每次都能成功送达!(即可靠性非 100%)
而失败的时候,就需要传输层来获取发生错误的原因,并进行错误重传或者超时重传等操作。
所以,在从网络层获取到信息成功送达到对方的反馈之前,传输层需要暂时保留已经发送的数据。如果网络层发送失败了,传输层需要进行重发操作,以保证可靠性。
1.2 主机、路由器、节点
- 主机:拥有 IP 地址,但不进行路由控制的设备
- 路由器:拥有 IP 地址,并进行路由控制的设备
- 节点:主机和路由器的统称
2.IP 报文
2.1 报文结构
IP 协议的报文与 TCP 的报文有一定程度上的相似
下图为 IPV4 中报文的格式(IPV6 的报文和下图是不同的)
对图中首部字段的解析如下:
- 四位版本号:指定 IP 协议的版本,IPV4 来说就是 4;
- 四位首部长度:IP 报头的长度,和 TCP 的定义一样,标准长度需要用
四位首部长度 * 4字节
,即最大报头长度为15*4=60
字节; - 八位服务类型(区分服务):包含 3 位优先权字段(已经弃用)、4 位 TOS 字段和 1 位保留字段(必须置为 0)
- 四位 TOS 分别表示:最大吞吐量、最高可靠性、最小成本、最小延时。只能四选一,需要根据具体传输层的协议要求进行选择;
- 只有区分服务的时候,这个字段才会启用,一般情况下都不使用这个字段;
- 十六位总长度:包括报头的整个报文的长度,减去四位首部长度代表的 IP 的报头长度,就能得出报文中数据字段的长度,最大值为 65535。
- 十六位首部校验和:使用
因特网检验和
来检测数据是否损坏(比 CRC 简单); - 八位协议:上层协议类型(比如 TCP、UDP 等)解决如何向上层交付的问题;
- 八位生存时间(TTL):用于控制 IP 报文能在网络层传输的时间(生命周期的限制)
- 比如有些报文由于路由时出现了错误,陷入了路由的死循环;亦或者是路由程序出现了 BUG,导致没办法被正常转发到正确的主机上;还有路由超时的问题。此时这个出错的报文就会在路由里面长时间游离而没办法抵达目的地(好比内存泄露)。
- 规定了 TTL 之后,可以在 IP 报头中中记录报文的生命周期时间,当报文转发的耗时已经大于这个 TTL 之后,就将这个报文丢弃。最大生存周期为 255 秒;
- 最初是以 IP 数据报中该字段的值减去报文在当前主机耗费的时间,若结果不为 0 则转发,否则丢弃;
- 现在以
跳数
为单位,路由器转发 IP 数据报的时候,将该字段值减一,如果减一后不为 0 则转发,否则丢弃
- 三十二位源地址和三十二位目标地址:表示发送端 IP 和接收端 IP
- 选项字段:不定长,最多 40 字节(这是因为首部默认是 20 字节,再加上首部长度最多只能表示 60 个字节,所以选项字段就是
60-20=40
字节) - 此处省略了标识、标志、片偏移字段,会在后文的分片中说明……
IP 协议的报文中没有端口号,因为端口号是传输层应该解决的事情(UDP 和 TCP 的报头中才有端口号的字段)IP 层只关注如何将报文发送给目标主机。也就是两个主机之间如何正常通信的问题。
2.2 分片
2.2.1 认识 MTU
MTU 相当于网络层发快递时对包裹尺寸的限制,这个限制是不同的数据链路层对应的物理层产生的限制。
- 以太网帧中的数据长度规定最小 46 字节,最大 1500 字节;
- ARP 数据包的长度不够 46 字节。要在后面补填充位;
- 最大值 1500 称为以太网的最大传输单元 (MTU),不同的网络类型有不同的 MTU;
- 如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的 MTU 了,则需要对数据包进行分片 (fragmentation);
- 不同的数据链路层标准的 MTU 是不同的;
- MTU 指上层能交付的数据的最大大小,其不包括以太网本身的报头。
所以,数据链路层不支持过大的数据,这就需要在网络层对数据进行分片。
而网络层 IP 协议会自动帮我们分片,并在接收端组装。这个行为对传输层来说是不需要关注的,在四层模型中,每一层之间的功能需要进行解耦。
分片之后,只要有一个分片报文丢失,这个报文的整体就会认为丢失了(因为没有办法拼出完整的报文数据)
但这样就引出了一个问题:分片会提高丢包的概率,影响传输速率(发一次快递和发三次快递,明显三次快递丢东西的可能性更高)
对于具有可靠性机制的 TCP 而言,丢包问题不大,我们可以在传输层进行重传。但 UDP 没有可靠性,此时出现分片后丢包了就没办法找回了。
所以,网络层进行分片并不是主流!相比之下,在传输层就进行分片才是更好的选择。
2.2.2 如何分片
在 IP 报头中,如下标识、标志、片偏移字段就是用于分片和组装操作的
- 十六位标识 (id): 唯一的标识主机发送的报文。如果 IP 报文在数据链路层被分片了,那么每一个片里面的这个 id 都是相同的
- 三位标志字段:
- 第一位保留 (保留的意思是现在不用, 但是还没想好说不定以后要用到)。
- 第二位 DF 为 1 表示 “禁止分片”, 这时候如果报文长度超过 MTU, IP 模块就会丢弃报文。
- 第三位 MF 表示” 更多分片”, 如果分片了的话, 最后一个分片置为 0, 其他分片报文都是 1;0 就类似于一个分片报文段的结束标记;
- 十三位分片偏移 (framegament offset): 是分片相对于原始 IP 报文的数据载荷开始处的偏移。 其实就是在表示当前分片在原完整数据载荷中处在哪个位置。实际偏移的字节数是这个值
* 8
得到的。因此,除了最后一个报文之外,其他报文的长度必须是 8 的整数倍 (否则报文就不连续了)
这一点非常重要!一定要记住:除了最后一个报文之外, 其他报文的数据载荷长度必须是 8 的整数倍。
如果数据载荷不足 8 的整数倍,需要进行向下取整!比如某个分片的数据载荷部分是 1476 字节,并非 8 的整数倍,那就应该选择
1476/8=184.5
向下取整,即采用8*184=1472
为该报文的数据载荷长度。
根据这三个分片相关字段,我们只要将三位标志字段中的更多分片MF
置为 1,就代表当前报文并不是一个完整的报文,而是已经被分片后的报文。
- 更多分片为 0,且分片偏移为 0,代表当前报文没有进行分片; 也可以判断 DF 禁止分片位,确认是否禁止分片;
- 更多分片为 1,且分片偏移为 0,代表当前是分片后报文中的第一个;
- 更多分片为 0,且分片偏移不为 0,代表当前是分片报文中的最后一个;
如果当前报文是分片后的,那就需要根据十六位标识来确定当前分片属于哪一个 “组”,再将当前报文和后续收到的 ID 相同的报文集合在一起,通过十三位片偏移来进行排序,组装成完整数据!
比如,第一个报文的起始偏移量是 0,第二个是 1000,第三个是 2000,这时候就根据片偏移排序进行拼接就可以了。(这只是个栗子)
如果对第二个分片还需要进一步分片,效果如下
分片练习题
下面是一个分片的例题,包含了偏移量的选择问题;
可以看到,一开始我们选择将原有的数据载荷拆成 780+700 的两部分,但通过计算得出,780/8=97.5
,并非 8 的整数倍,此时第二个 IP 报文中首部的偏移量字段就不符合要求了,所以我们要保证第一个报文中的数据载荷的长度是 8 的整数倍。
此时要采用向下取整的策略,即 780/8=97.5
,则我们采用 97*8=776
为第一个 IP 报文的数据载荷长度,第二个报文的数据载荷长度为 1480-776 = 704
,因为第二个报文是最后一个分片,所以它的数据载荷部分长度没有 8 的倍数的要求。
2.2.3 如何保证收完了一整个分片组?
根据十六位标识,按照片偏移进行排序,排序后发现缺失了部分的报文,那就代表没有被收完。因为每一个报文的偏移量+该报文长度
,就是下一个报文的偏移量
!只要数据对不上,那就代表丢东西了。
而开头和结尾的报文,就能通过上面提到的根据更多分片标记位+分片偏移
来确定有没有丢。
2.2.4 如何减少分片
虽然说 IP 网络层会自动帮我们分片,但是否分片是可以通过传输层来进行控制的
只要传输层一次交付的数据没有超过需要分片的阈值,那网络层在传输的时候就不会进行分片了!
减少分片的方式,那就是在传输层就进行一定的分片,这样能更好将丢分片报文
这件事在传输层进行处理。而不是在网络层丢包后,没办法在传输层失败并处理。
如果是 TCP 协议,在三次握手的时候,就会协商双方单次传输数据的大小(窗口大小)。从而避免网络层对数据进行分片,以规避数据链路层的 MTU 限制。同时也维护了滑动窗口,如果网络层的传输出现了丢包,由传输层来进行重传操作,以实现可靠传输。
- 限制成多少好呢?
一般建议将该大小设置为比网络中的最小 MTU 值小一些,以防止出现分片
2.2.5 分片的限制
在上文中提到,MTU 的限制是最大 1500 字节,这个数据长度是包含 IP 协议的报头的(数据是从 IP 网络层向下交付给数据链路层的)
假设我们有一个网络层的 3000 字节的数据,此时网络层要进行分片,并不是简单的 3000/1500=2
就能搞定了的。而是要计算上 IP 报头的长度(20 字节)
每一个分片都是一个独立的 IP 报文,都会有自己独立的 IP 报头!否则缺少报头,在接收端没有办法进行数据组装操作。
1 | 20+1480 |
一共需要分 3 片,才能将这网络层的 3000 字节的数据成功传输!
但是这里就有一个问题了,明明 1480+1480+20 = 2980
,并不是 3000 字节啊?
注意!上面提到的是网络层的 3000 字节数据。实际上,传输层只向下交付了 2980 字节,加上 IP 报头 20 字节才是网络层的 3000 字节数据。因为要进行分片,原本这 3000 字节的统一报头肯定是要丢弃的,我们需要操作的是传输层向下交付的 2980
字节数据,将其正确分片并添加上每一个分片的报头,再交给数据链路层。
2.3 TTL
上文提到 TTL 是用来控制报文的生命周期的,其为了避免报文在路由中出现死循环,又称路由环路。
比如下图中,假设有一个报文路由到了路由器 D,原本他应该被正常交付给主机 C,但路由器 D 出现了一些问题,将这个报文交付给了路由器 I,路由器 I 给 J,J 给 H,H 给 C,路由器 C 又给 D,路由器 D 还是有 bug,又转发给了路由器 I。这时候,就出现了一个报文路由的死循环。
如果没有 TTL 来控制生命周期,报文就会一直在这个死循环中跑,白白浪费路由器的性能!
规定了 TTL 之后,当报文的生命周期已经到了,但却还没有发送到目的地,那就需要将这个报文丢弃了。即超了 TTL 的时间就认为报文无效;
- 最初是以 IP 数据报中该字段的值减去报文在当前主机耗费的时间,若结果不为 0 则转发,否则丢弃;
- 现在以
跳数
为单位,路由器转发 IP 数据报的时候,将该字段值减一,如果减一后不为 0 则转发,否则丢弃
2.4 协议字段
协议字段占 8 比特,标识 IPV4 报文携带的是什么协议数据单元。
1 | ICMP 1 |
为什么协议字段里面会有 IPv6?因为 IPv4 到 IPv6 过渡的方法之一就是用 IPv4 报文来携带 IPv6 数据。另外一个过渡方案是双协议栈(即在机器上同时实现 IPv6 和 IPv4 的协议栈)
2.5 首部检验和
首部检验和采用的是因特网检验和,并非 CRC 检验。
IP 数据报每经过一次路由器,就需要重新计算首部检验和,因为某些字段(生存时间、标志、片偏移)可能会在传输过程中发生变化。
由于 IP 层本身并不提供可靠传输服务,而计算检验和是一个耗时的操作,所以在 IPv6 中路由器不再计算首部校验和,以更快的转发 IP 报文。
2.6 例题:从以太网帧字节流中读取目标 IP 地址
考研题目中会给你一堆字节流,让你以太网帧的从字节里面提取出 IP 地址。这需要我们对以太网帧格式和 IP 报文都深刻的了解。
- 以太网帧在 IP 报头之前添加了源 MAC、目标 MAC 地址、类型字段,一共 14 字节
- IP 报头中,在目的 IP 地址之前还有 16 个字节的内容
因此,我们将字节流中前 30 字节去掉,在读取 32 位(4 字节)即可得到目标主机的 IP 地址。
除了读取目的 IP 地址,题目还需要我们读取默认网关的 MAC 地址。而主机的默认网关一般都是路由器,该题转为读取以太网帧格式中的目的 MAC 地址。
第四题,该报文中 IP 分组经过路由器 R 转发的时候,应该需要修改源 IP 地址、生存时间、首部校验和字段。
如果是在不同的子网中进行转发,源 IP 地址不需要修改。如果是通过路由器的 WAN 口转发到上层局域网,则需要修改源 IP 地址。
如果路由器所连的两个局域网中 MTU 不同,可能需要对 IP 重新进行分片。
3. 网段划分
3.1 IP 如何找到对方主机?
IP 如何找到对方主机呢?
- IP = 目的网络 + 目的主机
- 由此还衍生出网段划分
以 IPV4 的 ip 为例,其格式为 1.1.1.1
,可以认为是下面的划分
1 | 1.1.1 .1 |
这就好比你的学号,前 X 位里面是学院的代码,最后才是班级 + 班级内编号。先找到你所在学院,再找到班级,最后再找到你。
反馈到 IP 里面,就是先找到网段,再找到主机。
3.1.1 发送数据的本质
IP 就是先找到目的的网段,再找这个网段中的目的主机。(先根据目的网段进行路由,找到目的网络,再通过主机号找到目的主机)
而查找目的主机的过程,本质是一个排除的过程。
先通过网段排除一个大类,再通过主机号来排除该网段中的单个主机。这样就能避免我们一个一个遍历在全网中查找主机,提高了查找的效率
子网划分的目的:就是提高查找目标主机的效率
这也是学校里面用学号的原因,除了为了给每个学生提供一个唯一标识,还能通过学号来提高查找到某一个学生的效率。
在全球互联网上,同样是通过 IP 地址的网段来划分国家,再划分到每个国家内部的不同区。这时候就会有一定 IP 地址资源的竞争。比如米国互联网发展早,下图中谷歌的服务器 IP 就老整齐了😂(不过这些服务器都在同一个地域,IP 很接近是合理的)
3.1.2 网络号和主机号
- 网络号:保证相互连接的两个网段具有不同的标识
- 主机号:在同一网段中,主机之间具有相同的网络号,但是主机号不同以进行区分
具体框架可以查看下图,192.168.128
就是这个局域网的网段 ,而最后的 10 和 11 是不同主机的两个主机标识。一般情况下,网段中的 1
号主机就是这个网段中的路由器。
我们家里的路由器除了进行路由转发,还有子网划分的功能。
如果出现了一个开头并非 192.168.128
的 IP,主机就能知道这不是当前局域网的 IP,于是就会将报文直接转发给 192.168.128.1
,让路由器去找这个 IP 的目标主机(进行跨局域网的下一层转发)
就好比我们的学号是学校的教务系统派发给每一位同学的,IP 中的网段也被 “某人” 在一定程度上根据地区进行了划分。
3.2 IP 地址类别划分
3.2.1 ABCDE
通过 A 到 E 类不同的划分,会衍生出不同范围的 IP 号,然后再分配给不同的国家或地区
1 | A类 0.0.0.0到127.255.255.255 |
依照上面的划分,如果我是一个大型企业,申请了一个 B 类的 IP 地址。此时就能支持我的局域网内 2^16
台主机的 ip 分配。但实际上我顶多会有 2w 个主机,此时就出现了 IP 地址的浪费!
3.2.2 CIDR 和子网掩码
为了避免上文中出现的 IP 浪费问题,CIDR(Classless Interdomain Routing)就出现了
- 引入一个额外的子网掩码 (subnet mask) 来区分网络号和主机号;
- 子网掩码也是一个 32 位的正整数。通常用一串
0
来结尾,一串1
开头; - 将 IP 地址和子网掩码进行 按位与 操作,得到的结果就是网络号;
- 网络号和主机号的划分与这个 IP 地址是 A 类、B 类还是 C 类无关
如果我们需要更多主机,就可以将子网掩码中最后一个 1 置 0,就能适配更多局域网主机。
所以,现在已经不用 ABCDE
的类别划分方式了,都采用了子网掩码方式。
根据上图可见,IP 地址与子网掩码做与运算可以得到网络号,主机号的二进制位从全 0 到全 1 就是子网的地址范围;
IP 地址和子网掩码还有一种更简洁的表示方法:例如 140.252.20.68/24
, 表示 IP 地址为 140.252.20.68
, 子网掩码的高 24 位是 1,也就是子网掩码 255.255.255.0
3.3 基础设施
有了网段划分,给不同国家和地区划分了 IP 之后,就需要有人来建设网络的基础设施
在我们国家,搞基础设施就是三大运营商(移动 电信 联通)
比如几年前做的 “光纤入户” 就是基础设施建设的一部分。
基础设施搭建好了后,再通过子网掩码和已经获取到的 IP 的网段来划分不同省份、不同市区;最终再落到每个入网用户的头上。
3.4 缓解 IP 地址不够用的办法
有人肯定会问了,现在公网 IPV4 的资源那么匮乏,大部分家宽都是没有公网 ip 的,那我们平时的上网是怎么实现的?
换句话说,如何缓解 IP 地址的匮乏?
- 可以使用
动态IP分配
技术,只给直接接入公网的设备分配 IP 地址,每一个设备接入网络时,其获取到的 IP 地址不一定和上次相同。 - NAT 技术(重点)
- IPV6(但是现在普及率有待提高)
IPV6 使用 16 字节(128 位)来标识一个 IP 地址,目前看来,2^128
位能给 “地球上每一粒沙子” 都分配一个 IP 地址了。虽然迟早也会有不够用的那一天,但至少是一个很不错的解决方案。我们国家就在大力推广 IPV6
通过前文的报文结构图可知,IPV4 的 IP 地址只有 4 字节(32 位)
4. 私有 IP
4.1 特殊的 IP 地址
但是,直接采用 CIDR 的方式作为局域网控制的方式,就容易出现混乱。而且即便是采用了子网掩码的方式,依旧可能出现 IP 不够用的情况(一位网民很可能有多个设备, 再加上各类智能终端,现在需要上网的设备只会越来越多)
需要注意的是 CIDR 只能提高 IP 地址的利用率,并不能提高 IP 地址的上限。
所以,就衍生出了部分特殊的 IP 地址。这些 IP 地址被规定只能用于局域网,由此来减少对公网 IP 的地址消耗。
- 将 IP 地址中的主机地址全设为 0,就是该地址的网络号,代表当前局域网
- 将 IP 地址中的主机地址全设为 1,就是广播地址,用于给同一个链路中相互连接的所有主机发送数据包。
127.*
的 IP 地址用于本机回环。我们通常使用127.0.0.1
来进行本地服务的访问和测试,该 IP 地址是 IPv4 回环地址的标准规定,IPv6 的回环地址为::1
。
我们在计算一个局域网中有多少设备的时候,需要减掉上文提到的网络号和广播地址。
以下是特殊的只能用于局域网的私有 IP 地址,包含在这个范围中的, 都称为私有 IP, 其余的则称为全局 IP (或公网 IP)
10.*
, 前 8 位是网络号,共16,777,216
个地址172.16.
到172.31.
,前 12 位是网络号,共1,048,576
个地址192.168.*
,前 16 位是网络号,共65,536
个地址
这里要记住 2 的 16 次方为 65536
,在网络的知识点里面经常会接触到这个数字。
4.2 loopback 环回
上文提到了 127.*
是用于本地环回的。下图是环回驱动程序针对 IP 地址的判断
在云服务器上执行 ifconfig
,也能看到本地环回的配置项;这里我们能发现,本地环回的 MTU 是远高于网络的 1500。毕竟是自己和自己通信,基本不会出现丢包,传输速度也是飞快,也就不用担心数据包太大的问题
同时也能发现,我们的云服务器被分配到的 ip 地址并不是云服务器的公网 ip,这也就表明了我们的云服务器并没有被直接暴露在公网上,而是通过了云服务器厂家的入网服务器(或者也叫路由器)来进行公网 ip 的映射和数据包的转发操作。
1 | eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 |
4.2.1 环回
上图中的环回驱动程序会直接和 IP 协议的接收端(即 IP 输入函数)相连,当检测到 127.0.0.1
的 IP 访问请求的时候,会直接把这个报文转发给 IP 输入函数,而不将其插入到以太网中。
就相当于你知道 127.0.0.1
这个 IP 地址代表的就是你自己,你想访问自己电脑上 8080 端口的程序,即便么有接入互联网的状况下也是能正常访问的!
Loopback 环回接口对于测试和诊断本地主机上的网络服务和应用程序非常有用,因为它可以模拟网络通信而不涉及实际的网络传输。
4.2.2 ARP
ARP 是一个在局域网数据链路层通过 IP 获取到局域网主机 MAC 地址的协议,具体请参考后文中的解析
4.3 访问广域网的步骤
4.3.1 说明
下图中能看到我们家用主机是怎么来进行广域网的访问的基本流程;
我们家里的路由器除了进行路由转发,还有子网划分的功能。可以看到左下角虽然是两个不同的家庭,但其可以分配出完全一样的局域网的子网 ip 192.168.1.1
,和不同的 WAN 口 IP(WAN 口就是路由器连接互联网的口)
这是因为我们的设备是直接和当前路由器相连的,访问的时候也只能通过当前路由器来进行局域网 IP 的转发。不可能会出现我访问一个局域网 IP,却跑到了别人家里的设备上的情况。因为这个局域网 IP 访问的报文并不会被转发到上层路由器上,也就不可能凭空飞到其他局域网中
图中的 122.77.241.3
就是一个公网 IP 的服务器,当我们需要访问这个主机的时候,局域网的家用路由器在检测到这个目的 IP 的时候,发现其并不是局域网的 IP 地址,于是就会将这个报文给转发给上层的运营商路由器。
运营商路由器是直接接入了公网 IP 的,其就能通过网段划分+主机编号
来查找目标主机,将报文转发给 122.77.241.3
服务器,再将服务器返回的信息转发给你的家用路由器,再转发到你的主机上。
这也告诉我们,想绕过运营商直接获得公网 IP 是不可能的,因为从物理层面上,我们的家用路由器就不是接在公网 IP 上的!即便是可以申请到的家用公网 IP,也和云服务器的入网服务器一样,是运营商的路由器分配给你的。
你会发现,家用申请的公网 ip,很多端口都是被屏蔽的(比如
80/443/8080
)这些端口的屏蔽操作,以及海外网站的「墙」也是运营商的路由器进行检测和屏蔽的!
4.3.2 内网访问公网的流程
假设我们的模型如下
1 | 广域网 |
下面是一个家用主机 A,访问公网 IP 的主机 122.77.241.10
的具体步骤;
1 | 本地主机A发送IP报文给家庭路由器B |
4.3.3 NAT 技术
这种不断替换源 IP 来进行路由转发的过程,就是 NAT 技术!
也正是 NAT 技术的存在,让我们能通过多级局域网来让更多的设备上网,大大缓解了公网 IP 的不足。
也正是因为 IPV4 地址不足的问题被大大缓解,推广 IPV6 就没有我们想象中的那么顺利了。毕竟 IPV6 的 IP 格式和 V4 完全不同,需要每个层级的路由器都进行功能升级,这可是一个巨大的工程!
4.3.4 NAT 怎么回来?NAPT
当目标主机收到这个报文后,他的反馈报文如下
1 | ———————————————————————— |
同样是先到达运营商路由器,运营商路由器需要缓存每一个转发到公网的报文的来源信息;为此路由器会维护一个转换表,记录着局域网主机的私有IP地址:端口号
与对应的公网IP地址:NAT端口号
的映射关系。
比如此次 TCP 链接中,我将路由器公网 IP 的 122.77.241.4:30000
映射给了局域网 10.1.1.2:40000
;当从公网收到服务器的响应报文后,从转换表里面就能够查到这个映射(一次通信中这个 NAT 映射是不会变的)从而确定该报文的局域网流向。
需要注意的是,NAT 技术在端口映射的时候不一定会映射到和内网主机相同的端口,此时不仅需要修改 IP 报文中的来源 IP,还需要进一步修改传输层(比如 TCP 和 UDP)中的源端口号
确定局域网 IP 后,就修改当前报文的目的 IP,继续往局域网转发;后面的子路由器也是如此,不再赘述。
1 | ———————————————————————— |
这种 IP:端口
的关联关系表,就是由支持 NAT 技术的路由器来维护的,这个转换表被称为 NAPT;当这次链接结束后,这对映射关系就会从转换表中被删除。
4.3.5 NAT 和代理服务器
代理服务器看上去和 NAT 设备有一定类似,客户端向代理服务器发送请求,代理服务器将请求转发给真正需要请求的服务器;服务器返回结果后,代理服务器把结果传回客户端。
4.3.5.1 NAT 和代理服务器的区别?
- 从应用来说,NAT 是属于网络基础设置,解决的是公网 IP 不足的问题;代理服务器更贴近具体应用,比如通过代理服务器进行 “翻墙” 和游戏加速;
- 从底层来讲,NAT 在网络层工作,对 IP 地址进行替换;代理服务器是在应用层工作;
- 从使用范围来讲,NAT 一般部署在局域网出口,代理服务器既可以在局域网又可以在广域网部署
- 从部署位置来看,NAT 集成在路由器或者防火墙的硬件上;代理服务器本质上是一个应用层软件,部署在服务器上
代理服务器应用相对来说也比较广
- 翻墙:广域网代理
- 负载均衡:局域网代理
4.3.5.2 反向代理
代理服务器分为正向代理和反向代理,这里来说说反向代理,反向代理处于目标服务器和客户端之间,客户端通过反向代理访问目标服务器,而不会直接连接到目标服务器
- 通过反向代理服务器作为一个云服务器主机集群的入网服务器
- 举例,一个网站拥有 10 台提供服务的服务器和一台反向代理服务器
- 域名解析到反向代理服务器
- 反向代理服务器通过服务器监控程序,获取到 10 台服务器中负载最低的那个,并将报文转发给它
- 反向代理服务器还会识别 10 台服务器中是否有宕机的服务器,如果有,则会告警给维护人员,并不再给这个宕机的服务器转发报文
- 反向代理服务器还可以设置黑白名单,当遇到 DDOS 攻击的时候,将来源 IP 放入黑名单进行屏蔽,避免过多的访问让所有服务器都宕机影响业务;将本公司的其他服务器放入白名单(一般白名单都很短)
反向代理的作用
- 通过反向代理服务器实现了负载均衡
- 便于统一管理服务器集群,提供统一入网服务器
- 避免了直接将提供服务的主机 IP 暴露在公网上而被直接攻击的问题,提高了安全性。
- 反向代理还能对网页内容进行一定的缓存,从而减轻后端服务器的负担(比如缓存网页前端的静态资源,当用户访问的时候,由代理服务器直接返回资源,而不需要去请求实际提供服务的服务器)
总之好处多多!
4.3.5.3 正向代理
正向代理是位于客户端和目标服务器之间的中间服务器。客户端通过正向代理来访问互联网上的资源,而不是直接连接到目标服务器。正向代理的作用主要有以下几点:
- 访问控制和过滤: 正向代理可以用于限制用户访问特定网站或资源,以实施访问控制策略。它可以过滤不良内容,提供更好的安全性和隐私保护。
- 隐藏客户端身份: 正向代理可以隐藏客户端的真实 IP 地址,从而保护用户的隐私和匿名性。目标服务器只能看到代理服务器的 IP 地址,而无法获取到真正的客户端信息。
- 缓存和加速: 正向代理服务器可以缓存常用的内容,从而减少对目标服务器的请求,提高访问速度。
- 突破防火墙限制: 在一些网络环境中,访问特定网站可能受到限制或封锁。使用正向代理可以绕过这些限制,访问被封锁的资源。
4.4 ISP 检测宽带账户
下文中的部分内容来自 chatgpt,我对里面的内容进行了补充和修改
运营商的路由器还会检测我们的账户是否还有余额。我们的家用路由器一般是通过光猫登录了自己的宽带账户;也可以将光猫设置成桥接模式,将接入光猫的路由器设置为宽带帐号(PPPoE)上网方式,登录运营商提供的宽带账户和密码,来接入网络。
ISP 的路由器检测你的宽带账户通常是通过以下步骤来完成的:
- 设备连接与识别: 当你的计算机或其他网络设备连接到 ISP 提供的路由器时,路由器会分配一个私有 IP 地址给你的设备。这个 IP 地址是由路由器动态分配的,并且通常处于特定的私有 IP 地址范围,比如
10.11.1.0/16
或10.11.1.0/24
等。此时,你的设备与路由器建立了一个大局域网内的连接。(运营商并不会直接给你分配192.168.*
这样的私有 IP,因为这个 IP 一般是用于最底层局域网的,给你分配了,那家庭局域网的就没 IP 用了) - 认证过程: 在你尝试访问互联网时,ISP 的路由器会引导你的设备进行认证过程。这通常涉及向 ISP 服务器发送你的宽带账户的登录凭据(例如,用户名和密码)。
- 账户验证: ISP 的服务器会验证你提供的登录凭据是否与其记录中的账户匹配。如果验证成功,说明你的宽带账户是有效的,路由器会为你的设备分配一个公共 IP 地址,这个 IP 地址是可供互联网访问的。
- IP 地址分配: 一旦认证成功,ISP 的路由器会为你的设备分配一个公共 IP 地址。这个 IP 地址是在互联网上唯一标识你的设备的地址,使你的设备可以与其他互联网设备进行通信。
- 数据传输: 一旦有了公共 IP 地址,你的设备就可以与互联网上的其他设备进行通信,发送和接收数据。
这样,通过认证和 IP 地址分配的过程,ISP 的路由器可以检测并识别你的宽带账户,从而让你的设备能够访问互联网。
人话就是,登录了宽带账户以后,当我们路由器发送的报文交付到运营商路由器的时候,该路由器就会检测你这个宽带账户的余额。如果没有余额了,就会直接丢弃掉你的 IP 报文。我们看到的结果就是无法上网!
这个操作并不是每次都会执行的,只要你的路由器能稳定的接入到运营商的路由器上,那就不会每次都进行宽带账户的验证,否则会增加网络的负担。
具体的验证流程都是运营商路由器和你的本地光猫自动完成的。
而手机没有话费余额的时候,我们依旧能拨通诸如 120、119 等紧急号码,这也是运营商的服务对这些特殊的电话号码做了类似于免费白名单
的操作(手机号码可以类比公网 IP 来理解)
5. 域名
5.1 DNS 服务器
上文讲述的都是关于 IP 协议的事情。但实际上我们日常生活中,一般都不会直接使用 IP 地址 + 端口号的方式来访问某一个服务,而是使用域名来访问。比如
1 | www.baidu.com |
所谓域名,就是这些英文字符串和 IP 的映射。
比如 baidu.com
就是一个域名,而 www.baidu.com
是该域名下的三级域名(几级域名可以看有几个点)
实际访问的时候,是百度在域名注册商哪里,将 www.baidu.com
指向了自己的服务器的地址 (假设指向的是 1.1.1.1
) 我们访问百度,实际上访问的就是 IP 地址 1.1.1.1
;
- DNS 是应用层协议,底层使用的是 UDP
- 系统会缓存一部分 DNS 的结果
在主机本地,有一个 hosts
文件,也可以用于设置主机到域名的映射,在 linux 里面就是 /etc/hosts
这个文件。 在访问域名的时候,操作系统会先检查自己本机器的 hosts,如果本地没有,就请求 DNS 服务器来获取解析结果。
5.2 域名访问主机流程
当我们访问一个域名的时候,首先会去请求特殊的 DNS 服务器
1 | 8.8.8.8 谷歌公司的DNS服务器 |
先请求这些 DNS 服务器,服务器内会针对域名查询对应的 DNS 解析,最后再访问该解析对应的 IP 地址
而使用域名的时候,默认访问的是该主机的 80(HTTP)/443(HTTPS)
端口 ,我们也可以像 IP 一样,在域名之后用:端口
来指定特定端口号进行访问,即域名:端口
。
一般情况下,我们的主机都可以通过自动配置 DNS 从上层路由器中获取到 DNS 服务器的地址(比如运营商会在路由器基站中内置 DNS 服务器)
5.2.1 DNS 劫持
所谓 DNS 污染和 DNS 劫持,就是因为我们访问的 DNS 服务器的时候,获取到的结果和预期不同,从而导致无法访问目标网站,或者访问了假的目标网站
1 | www.baidu.com 明明应该指向 1.1.1.1 |
坏蛋可以在 1.1.1.3
服务器上,搭建一个和百度「看起来」一模一样的页面,并将你的报文给转发到百度服务器上。此时他就通过中间转发,获取到了你报文中的用户信息、密码等等参数;
这时候因为这个假的服务器是直接给你提供服务了,使用的 HTTPS 证书也是这个假服务器的证书,对方可以直接通过自己的证书解密获取到你的信息,再转发给百度。
5.2.2 DNS 和负载均衡
对于大公司而言,DNS 解析还有一个重要的作用,就是通过不同区域的设置来实现负载的均衡。
假设百度在每一个省份都设立了一个自己的机房,那么它就可以通过 DNS 服务器,当不同省份的用户请求服务器的时候,返回他当前所处省份的机房地址。这时候就实现了每个机房的负载均衡。
在现实中,就是将你的请求转接到离你最近的拥有机房的省份,这样既能保证所有服务器的负载均衡,又能保证你的访问能较快地获取到响应(广东访问广州的服务器,肯定比访问北京服务器的延迟低一些)
5.3 DNS 分层
DNS 服务器不会存放所有已知域名的 IP 解析,因为互联网上存在大量的域名,数量庞大且不断增长,单一 DNS 服务器无法存储和处理所有域名的 IP 映射。
实际上,DNS 服务器通过分层的架构来解决这个问题。在根 DNS 服务器层级,有一组全球性的顶级 DNS 服务器,它们存储顶级域名(例如.com、.org、.net
等)的 IP 地址。然后,在每个顶级域名下,有其他 DNS 服务器,负责管理该顶级域名下的子域名(例如,google.com、facebook.com
等)。这个过程继续向下,形成了一个层级结构。
当你的设备需要解析某个域名时,它首先会向本地 DNS 服务器(通常由你的 ISP,即互联网服务提供商,人话就是国内的三大运营商提供)发起请求。如果本地 DNS 服务器知道所需域名的 IP 地址,它会直接返回该 IP 地址给你的设备。但如果本地 DNS 服务器不知道该域名的 IP 地址,它会向根 DNS 服务器发起请求。
根 DNS 服务器将指导本地 DNS 服务器转向相应的顶级 DNS 服务器。然后,顶级 DNS 服务器可能会进一步将请求转发给负责该特定域名的授权 DNS 服务器。最终,这个授权 DNS 服务器将返回所需域名的 IP 地址给本地 DNS 服务器,本地 DNS 服务器再将其返回给你的设备。
这个层级结构允许 DNS 系统更高效地处理大量的域名解析请求,并确保及时更新和管理域名与 IP 地址的映射。所以,单个 DNS 服务器并不会存放所有已知域名的 IP 解析,而是通过层级结构来分散和管理这些信息。
5.4 dig 工具分析 DNS 解析过程
1 | yum install bind-utils |
安装了之后就可以使用 dig 命令来查看域名解析过程了
1 | dig 域名 |
6. 路由
路由的过程,就是下图这样一跳一跳 (Hop by Hop) “问路” 的过程
所谓 “一跳” 就是数据链路层中的一个区间。具体在以太网中,指从源 MAC 地址到目的 MAC 地址之间的帧传输区间。
6.1 问路栗子(帮助理解)
拿日常生活中问路来举例子(请屏蔽现在有导航这件事)一般问路,会得到三种结果:
- 我不知道
- 我知道 XXX 知道
- 我不知道,但是给你乱指(在路由寻址的过程中,这个情况是不存在的)
比如张三要去南京大学的仙林校区,他飞机落地南京后,不知道怎么走;他找了个机场的保安,问他 “我是从 xx 省新来的大学生,应该怎么去南京大学?” 保安让他先坐 xx 路公交车去仙林大学城,到哪里再去问其他人。
张三到了仙林大学城后,又找到了路边的环卫工,又问 “我从机场过来,应该怎么去南京大学。” 环卫工给他指了条明路,那里就是南京大学的教学楼,入口就在这附近。这时候张三获取了两个人(路由器)的帮助,成功递达了南京大学的仙林校区(目标主机)。
可以看到,问路和上面图示中 IP 报文的路由是很相似的,都是一个路由一个路由的 “问路”,最终找到目标主机。
6.2 路由表
报文在各个路由器之间路由也是如此。当一个路由器遇到一个 IP 报文
- 他首先检测这是不是自己分配的局域网中的 IP,是则可以直接转发到目标主机;
- 不是则查看自己的路由表,看看自己是否保存了这个 IP 应该往哪里走;
- 如果它自己不知道,则可以和其他与自己相连的路由器通信(信息同步),问他们这个 IP 应该给谁(就好比你在公司里面问某一个工作应该交给谁去做)
- 如果周边的路由(旁边的路人)都不知道该往哪里去,则路由器会将其交付给自己的
默认路由
,报文继续转发,去下一个人那里问路了。
这样的跳跃都会有一个前提条件:相邻的两个主机(或路由器)一定是在物理上相连,处于同一局域网之中。每次的跳跃,本质上是从一个子网跳到另外一个子网;广域网可以认为是最大的 “子网”
6.3 route 命令
在 windows 电脑上可以使用
route print
命令打印路由表,本文不关注 windows 平台。
在 linux 平台里面,可以通过 route
命令查看当前主机的路由表,在 Centos8 主机上,命令显示如下。
可以看到,路由表的名字叫做 内核 IP 路由表,这也是 linux 系统中的一个内核数据结构。内部维护了路由表的目的地、Gateway 网关、Genmask 掩码、Flags 标志位、Metric、Ref、Use、Iface 接口
1 | Kernel IP routing table |
- Flags 中,U 代表当前路由表的条目是有效的,G 代表其是一个路由器。
- Gateway 中,
_gateway
以及0.0.0.0
代表的都是默认网关 - Iface 中,只有
eth0
是一个真正的物理网络接口,而docker0/br-*
都是 docker 容器虚拟出来的桥接网络接口
假设我我们有一个目的 IP 是 172.16.0.2
,获取到这个 IP 后,系统就会将其和路由表中的子网掩码依次进行按位与;
在 linux 环境下,可以使用如下代码来进行这两个 IP 的按位与操作。其中 inet_addr 和 inet_ntoa
是 linux 下的两个系统调用接口。用于 IP 字符串到无符号整数之间的转换,具体的介绍可以阅读我的 UDP 博客
1 |
|
二者输出结果都是 172.16.0.0
得到输出结果后,再和路由表中该项的 Destination
进行对比,二者相等,代表当前 IP 就是需要通过该项进行路由,那就将这个报文通过 Iface
接口 eth0
发送出去就 OK 了
1 | Destination Gateway Genmask Flags Metric Ref Use Iface |
如果按位与的结果与 Destination
匹配不上,那就继续往下一个条目的子网掩码进行按位与。如果整个表都按位与完毕了,还没找到该去的地方,那就将其通过 default
默认路由发送出去。
7. 数据链路层
前面谈了一大堆将数据从一个主机到路由器再跨越多个路由器递达目标主机的流程。
要想实现这一点,我们还需要数据链路层的帮助,即实现同一局域网内两台主机在物理层面上的相互通信。
- IP 网络层:提供跨网络找到正确路由路径,并传输数据的能力
- 数据链路层:提供将数据在同一网络中传输的能力
- 物理层:实际上的数据在物理(网线)上传输
数据链路层也有不同的传输方式,本文主要关注当前主流的以太网;
需要注意的是,以太网不是一种具体的网络,而是一种技术标准。它即包含了数据链路层的内容,也包含了一些物理层的规定,比如拓扑结构和访问控制方式,传输速率等。
注意:本文中关于数据链路层的知识并不完整,因为当时的学习目标是最常用、面试最常考的网络知识,并没有完整学习计算机网络。
2023-12-17:后续计算机网络的完整总结会根据 B 站视频的学习单独开一个博客。
7.1 认识局域网的基本情况
7.1.1 局域网认识
不过在认识以太网之前,我们需要先知道局域网通信的一个基本情况;假设下图中就是一个局域网,其中包含了不同的主机
首先,如果想要两台主机能够通信,我们就需要先将其链接到同一根网线上(在这里暂且不管 WIFI,其实本质上也是连在了这根网线上);这就好比进程间通信的时候,你需要先能看到同一份资源,才能实现对这份资源的共享访问。
虽然我们认为在局域网里面通信的时候,是两台主机直接交流,但实际上你可以把局域网当作一个教师,当张三和李四沟通的时候,其实会有很多其他的吃瓜群众都能接收到你发送的这个信息。
反馈到以太网报文上,因为每个主机都是知道自己的地址的,所以只要检测到以太网报头中的目的地址
不是自己的时候,就可以丢弃掉这个报文。因为目标并不在和你说话。这就好比在教室里面,你隔壁有俩人在聊天,他们并没有和你交谈,所以你可以不用管他们聊了什么。
这也反馈出了为什么某些公共 WIFI 会不安全,因为只要接入了这个 WIFI,你就有办法检测到其他用户通过这个局域网发送了什么信息!
因为局域网内的主机是通过目的地址判断有没有人和自己聊天的,所以即便我们的电脑开机后什么事情都没有干,在操作系统底层(数据链路层)其实一直都在从局域网中拿到新的数据链路层报文,并检测是否是发给自己的报文:
- 是,向上交付
- 否,丢掉
7.1.2 碰撞域
由于数据链路层向下是直接交付给物理层的,在物理层(网线)中光电信号传输是不能同时传输多个数据的,这就要求我们同一个局域网的多台主机不能同时往局域网中发数据。为了解决这个问题,主机引入了休眠机制,通过不同时间的错开休眠,来避免两台主机同时往局域网中发数据的情况。
- 理想情况:同一时间只有一台主机在局域网中发送数据
- 碰撞问题:如果出现了同时发送,数据出现冲突,就需要剔除掉这部分数据
通过碰撞域
解决数据冲突问题,尽量达到理想情况;
比如我们的交换机就有划分碰撞域的功能。接到交换机上的设备,除了通过交换机进行路由转发,如果在交换机的这部分设备中出现了数据碰撞,那么交换机就能把碰撞控制在当前这个小的碰撞域内,而不会向更大的局域网中传播。
如果一个局域网里面只有一个交换机(路由器)那么这整个局域网共享碰撞域
所以大公司内为了避免局域网因为碰撞问题而导致的网络卡顿,一般都会将几台电脑接入一个小的交换机中来划分碰撞域。
如上是物理层面的事情,软件层面上,一个 MAC 帧不要太大,否则会大大增加碰撞的概率。所以 MAC 帧必须要对上层交付的数据大小提一个要求,不能交付太大的数据,这就是 MTU 的由来(一般都是 1500 字节,至于为什么是 1500,那就是学术层面的事情了)
7.1.3 链路和数据链路
链路是一条物理的链路,中间没有任何交换节点;
数据链路是基于链路上的,需要有协议来实现数据的传输;将这些协议和硬件加入到链路上,就行程了数据链路;
7.2 以太网帧格式(MAC 帧)
7.2.1 MAC 帧格式
这部分的知识可以小结如下
以太网 MAC 帧格式分为下面两种,这两种格式仅类型字段有区别
- DIX Ethernet V2(流行,后文涉及到的是这个)
- IEEE 802.3
其中以太网 V2 的 MAC 帧格式如下
- 目的地址(6B):48 位的 MAC 地址,MAC 是每个主机在局域网内唯一的身份标识
- 源地址(6B):48 位的 MAC 地址,同上
- 类型(2B):上层(网络层)使用的协议,帧协议类型有三种值,分别对应
IP、ARP、RARP
,所以只需要 2 个字节- TCP/IPv4:0x0800
- ARP 请求或响应:0x0806
- RARP:0x8035
- Novell 网络层 IPX 协议:0x8137(这个了解即可)
- 数据载荷:46B-1500B,满足最小帧长 64B 和最大帧长 1518B 的要求
- FCS(4B):使用 CRC-32 校验计算出来是帧检验序列(计算包括了除 FCS 字段的全部内容)
这便就是以太网固定添加的报头,在进行解包的时候,我们只需要取走数据最前面的 14 个字节(6+6+2),再丢弃末尾的 4 个 FCS 的字节,就能取到上层(网络层)的原始数据。
7.2.2 MAC 帧中的 MTU
请注意,数据链路层的 MTU 是 1500,其实是 IP 可以交付的(包括 IP 报头)的长度是 1500,这个 1500 是不包含以太网帧的报头的!
以太网帧的报头是 18 字节(包括 MAC 头部和 FCS),如上图所示。实际上能传输的数据大小是 [64 -18, 1518 - 18] = [46, 1500]
;
这个限制是为了满足数据链路层中最大帧/最小帧
的要求,其中如果有效载荷不满足 46 字节,则会自动补 0;多余的部分需要在网络层进行分片。
7.2.3 物理层 8 字节前导码
在以太网 V2 的物理层,还会给 MAC 帧前加上 8 个字节的前导码;
- 前 7 字节为前同步码
- 后 1 字节为帧开始定界符(以太网帧中没有帧定界符)
7.2.4 不可靠传输
以太网 v2 的数据链路层不提供可靠传输机制,遇到错误会直接丢弃。
- MAC 帧长度不是整数字节
- MAC 帧的 FCS 字段检验出错
- MAC 帧长度不在
[64,1518]
范围内
7.2.5 FCS 字段
MAC 帧中的 FCS 字段为 4 字节(32 比特),采用是 CRC 校验码是 CRC-32;
7.3 认识 MAC 地址
MAC 地址就好比网络层里面的 IP 地址,是用来标识数据链路层中的主机的。
- MAC 地址用来识别数据链路层中相连的节点;
- MAC 地址长度为 48 位(6 个字节)一般用十六进制加上冒号的方式来表示,例如
08:00:27:03:fb:19
- MAC 地址在网卡出场的时候就确定了,不能被修改。虚拟机中的 MAC 地址并不是真实的 MAC 地址,可能会和已有的冲突(但虚拟机会检测冲突并及时修改,不然虚拟机就上不了网了)也有很少部分网卡支持用户配置 MAC 地址
MAC 地址又称为物理地址
,当你看到物理地址这个概念时,需要知道它指的是数据链路层里面的 MAC 地址,并不是物理层里面的概念!
7.3.1 MAC 地址和 IP 协议的区别
MAC 地址和 IP 地址的区别如下:
- IP 地址描述的是路途总体的起点和终点
- MAC 地址描述的是路途上每一个小路由区间的起点和终点
因为我们的主机不可能知道一个很远的内网主机的 MAC 地址,所以就需要 MAC 地址在小路由区间来标识起点和终点,并实现正确的数据传输。
7.3.2 MAC 地址的唯一性
这里需要知道一个小知识,虽然 MAC 地址在一定程度上可以认为是全球唯一的,但实际上只需要保证同一个局域网内的 MAC 地址是唯一的,就 OK 了
7.4 MTU 对上层的影响
7.4.1 MTU 对 IP 的影响
在前文提到过,为了避免光电信号在物理层传输的时候出现冲突,需要限制网络层给数据链路层传输的单次的数据大小,MTU 的具体说明可以参考本文 2.2.1 认识 MTU;
因为 MTU 的存在,网络层 IP 协议中需要对较大的数据包进行分包(IP 分片和组装问题在上文也谈过了,这里就不重复了)
但因为 IP 协议层分片和组装对于传输层来说是不可见的,如果 IP 分片后出现丢包导致数据丢失,那么传输层就必须得重传。所以传输层为了避免这种不受自己控制的事情,最终分片的操作应该是由传输层来进行处理才是最好的;
7.4.2 MTU 对 UDP 的影响
UDP 最大可以传输数据是 2^16
字节,也就是 64KB
,而 1500 字节是 1.5KB
;也就是说,只要 UDP 携带的数据超过 1472 (1500 - 20 IP首部 - 8 UDP首部)
,那么就会在网络层被分为多个 IP 数据报。
一旦这个数据报中有一个 IP 报文丢失了,那么整个 UDP 报文就会丢失。再加上 UDP 并没有超时重传机制(不过可以根据具体的协议来定制应答和重传机制来保证数据可靠性),UDP 的报文在 IP 层中被分片后丢包的概率远大于 TCP
7.4.3 MTU 对 TCP 的影响
- TCP 的一个数据报也不能无限大,还是受制于 MTU
- TCP 的单个数据报的最大消息长度,称为
MSS(Max Segment Size)
; - TCP 在建立连接的过程中,通信双方会进行 MSS 协商。最理想的情况, MSS 的值正好是在 IP 不会被分片处理的最大长度 (这个长度仍然是受制于数据链路层的 MTU)。
- 双方在发送 SYN 的时候会在 TCP 头部写入自己能支持的 MSS 值。然后双方得知对方的 MSS 值之后,选择较小的作为最终 MSS,进行数据传输;
- MSS 的值在 TCP 首部的 40 字节变长选项中
(kind=2)
;
7.4.4 MSS
除了 MTU,还有另外一个概念是 MSS,既应用层中有效数据部分的长度
- MAC 帧中有效数据长度是
[46,1500]
;
7.5 ARP 协议
ARP 协议属于数据链路层,可以认为是 MAC 帧协议的 “上层”
7.5.1 说明
因为在局域网内的传输时,我们是用 MAC 地址来作为不同主机的标识符的,所以就必须存在一个 IP 地址到 MAC 地址的转换。
ARP 协议也是包含在以太网帧格式这个整体概念中的,其中属于 ARP 自己的正文只有 28 个字节;因为 MTU 限制最小的数据长度是 46 字节,所以在发送 ARP 报文的时候,需要给这个 28 字节后面填补空位。
先来看看 ARP 请求 / 应答中的各个字段的含义吧
- 硬件类型:标定底层使用的是以太网还是其他帧格式,1 为以太网
- 协议类型:指要转换的地址类型,
0X0800
为 IP 地址 - 硬件地址长度:对于以太网来说为 6 字节(这里填的是数字 6,所以只需要占用 1 个字节);
- 协议地址长度:对于 IP 地址来说为 4 字节(同上);
op
字段为 1 表示 ARP 请求,2 表示 ARP 应答
当我们的主机开始发送报文之前,我们的主机是不知道某一个 IP 对于的目标主机的 MAC 地址的。所以就需要用 ARP 协议向局域网内发送一个请求,并得到目标主机的 ARP 响应,响应中就包含了该主机的 MAC 地址
7.5.2 ARP 请求 / 响应的流程
主机 A 需要给主机 B 发送数据,但是不知道主机 B 的 MAC 地址,它就需要发起一个 ARP 请求:
- 以太网目的地址填为全 F,代表广播;源地址填自己的 MAC 地址
- 帧类型填
0806
代表 ARP 协议; - ARP 中的 OP 填为 1,代表是 ARP 请求;
- 发送端以太网地址和 IP 地址填为主机 A 自己的 MAC 地址和自己的 IP 地址;
- 目的以太网地址填为全 F,IP 地址填为目的的 IP 地址(注意我们是知道对方 IP 地址的);
- 向下交付给以太网的 MAC 帧,然后送入局域网;
这个 ARP 请求的报文就开始在局域网内进行广播
- 收到这个 ARP 请求的主机在 MAC 帧收到,并向上交付给自己的 ARP 层;
- ARP 层先通过 OP 为 1 判断是 ARP 的请求;
- 取出请求中的目的 IP 地址,判断是否为本机 IP 地址,不是则丢弃;
- 是本机的 IP 地址,代表这个报文是发给自己的 ARP 请求,需要构造 ARP 响应;
于是主机 B 就收到了这个 ARP 请求,并开始构造 ARP 响应
- 以太网目的地址填为 ARP 请求中的发送端 MAC 地址,源 MAC 地址填自己的
- 帧类型填
0806
代表 ARP 协议 - ARP 中的 OP 填 2,代表 ARP 响应
- 发送端以太网地址和 IP 地址填为主机 B 自己的 MAC 地址和自己的 IP 地址
- 目的以太网地址填为 A 的 MAC 地址(在 ARP 请求中得知的)IP 地址填为目的主机 A 的 IP 地址
- 向下交付给以太网的 MAC 帧,然后送入局域网
此时这个 ARP 的响应就开始在局域网中传输,因为此时以太网的目的地址不再是全 F,所以各个收到这个报文的主机,就可以直接通过 MAC 地址来判断是否是发给自己的 MAC 帧。如果不是就直接丢弃,不交付给上层;
主机 A 判断目的 MAC 帧是自己的,交付给上层的 ARP 协议
- 判断 ARP 中的 OP 为 2,代表是 ARP 响应
- 因为前面已经判断过目的 MAC 地址是自己的了,所以这时候不需要判断 ARP 中的目的 MAC 和目的主机 IP 了
- 直接取出发送端主机 MAC 和主机 IP,就能得到主机 B 的 IP 和 MAC 地址的映射关系
这时候主机 A 就得到了主机 B 的 MAC 地址,可以正常进行数据的发送了!
7.5.3 ARP 缓存与更新
- 任何一台主机发出的一定是 ARP 的请求;
- 接收到的 ARP 可能是对方向往发送的 ARP 请求,也可能是我发送的 ARP 请求的响应;
但如果每台主机都不知道 IP 和 MAC 的映射关系,岂不是每次发送数据之前,都需要来个 ARP 请求和响应来获取对方 MAC 地址?这样整个局域网内就得被 ARP 请求和响应给塞满了。
所以,当我们发送了一个 ARP 请求后,应该需要将 ARP 响应给暂时缓存到本机上,避免下次发送的时候不知道对方的 MAC 地址。操作系统中就有一张 ARP 缓存表,保存了局域网内部分主机的 IP 和 MAC 地址的映射关系。
如果一个主机想获取到局域网内所有的 MAC 地址,就可以写个循环,把局域网内的所有 IP 都发送一次 ARP 请求,再将收集到的 ARP 响应给缓存起来(因为局域网内的主机网络号都是相同的,主机号都是是从 1 到 254,并不多,写个循环就行了)
但这里会有一个问题:如果某台主机 B 离开了你这个局域网,主机 C 接入后,路由器给主机 C 分配了原本是给主机 B 用的 IP,这时候主机 A 里面的 ARP 缓存表没有更新,还是填了主机 B 的 MAC 地址(但主机 B 其实已经不在局域网里面了),这时候这个报文岂不是找不到目标主机了?
所以 ARP 不仅仅需要缓存,还需要保有一定的更新机制:可以设置一个定时器,定时向缓存表中已有 IP 的主机发送一条 ARP 请求,并得到对方的 ARP 响应。这时候就可以比对返回的 MAC 地址是否有变动,有变动则更新。
如果一个 ARP 请求长时间没有得到响应,则可以认为该 IP 地址目前没有对应的主机,将其从缓存表中删除。
7.5.4 中间人攻击
上文讲述了 ARP 的请求和响应的格式,假设出现了下面的这个情况:
- 主机 A 想获取主机 B 的 MAC,发送 ARP 请求并获取到了响应
- 主机 D 把自己的 IP 伪装成主机 B 的 IP,又给主机 A 发送了 ARP 响应
- 此时主机 A 更新了 ARP 缓存表,将主机 B 的 IP B 映射给了 MAC D
- 主机 D 用同样的办法,将主机 B 的 ARP 缓存表中的 IP A 映射给了 MAC D
- 此时主机 A 给 B 发送消息,填的是 MAC D;B 给 A 发消息,填的也是 MAC D;
- 主机 D 在收到 A 和 B 双方通信的报文后,先交付给自己的上层,再转发给对方;
- A 和 B 的双方通信就出现了一个中间人 D,此时如果你的信息不是加密的,那就可以被主机 D 所窃取!
这也是为啥出现了 https 来避免中间人攻击!
7.5.5 RARP
RARP(Reverse Address Resolution Protocol,逆地址解析协议)是一种网络协议,用于在局域网(LAN)中通过已知的物理地址查找相应的 IP 地址。
与前面讲述的 ARP(Address Resolution Protocol,地址解析协议)不同,ARP 用于通过已知的 IP 地址查找相应的物理地址,通常用于将网络层(IP)地址映射到链路层(MAC)地址。RARP 则执行相反的操作,它允许主机在启动时使用其物理地址来请求分配给它的 IP 地址。
RARP 协议在过去的计算机网络中用于在没有人工配置的情况下为计算机分配 IP 地址。当计算机启动时,它会向网络发送一个 RARP 请求包,其中包含它的物理地址(MAC 地址),以请求分配一个 IP 地址。网络中的 RARP 服务器会接收这个请求并回复包含 IP 地址的 RARP 响应包。
然而,随着时间的推移,RARP 的使用逐渐减少,主要是因为它的局限性,例如不太适用于大型网络,以及需要特定的服务器来管理地址分配。现代的网络通常使用 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)来实现类似的功能,它更灵活且易于管理,可以自动分配 IP 地址以及其他网络配置参数给主机。
7.6 虚拟局域网 VLAN
7.6.1 问题说明
我们常用的以太网交换机工作在数据链路层 + 物理层,使用以太网交换机互连起来的交换式以太网,其所有站点都属于同一个广播域。
但是巨大的广播域会造成一些问题,比如难以管理和维护、潜在安全问题(一个设备可以攻击整个巨大的广播域中的所有设备)、广播风暴。
当局域网内的 A 要给 B 发送一个 MAC 帧的时候,首先需要用 ARP 协议获取目标设备的 MAC 地址。此时这个 ARP 请求就会被局域网内的所有设备都收到,每个设备收到 ARP 请求需要判断它是不是发给自己的,这就造成了无效的 CPU 资源浪费
如果局域网内不会频繁出现广播信息,这个问题还不是很严重。但是当前使用的很多协议都会发出广播,网络中会频繁出现广播信息,这就很难受了。
为了解决这个问题,引入了虚拟局域网 VLAN 技术,来将一个大的广播域划分为各个小的广播域。
除了使用虚拟局域网,还可以使用路由器来隔离广播域。但是路由器成本较高,虚拟局域网只需要在支持的交换机中进行设置即可,成本较低。
7.6.2 虚拟局域网 VLAN
虚拟机局域网 Virtul Local Area Network 是一种将局域网内设备化为与物理位置无关的逻辑组的技术,让这些逻辑组成为一个独立的逻辑上的局域网。
如下图,使用 VLAN1 和 VLAN2 对设备进行隔离后,只有同一个 VLAN 里面的设备才能相互通信,不同 VLAN 直接不会收到相互的广播域。
7.6.3 VLAN 实现机制
VLAN 标记字段
交换机在收到一个常规的 MAC 帧后,会在其源 MAC 地址字段和类型字段之间插入一个 4 字节的 VLAN 标记字段,将 MAC 帧转为了 IEEE 802.1Q
帧。这个 VLAN 标记字段能告诉我们当前这个 MAC 帧属于哪一个 VLAN,从而实现 VLAN 隔离。
VLAN 标记中的最后 12 比特为 VLAN 标识符 VID,它唯一的标识了以太网帧属于哪一个 VLAN。
- 取值范围是 0 到 4095(0 到 2 的 12 次方 - 1)
- 但是 0 和 4095 都不使用,实际可用为 1 到 4094
注意,这个操作是由交换机来实现的,而不是用户主机实现的。
交换机端口类型
交换机的端口类型有下面三种
- Access
- Trunk
- Hybrid
在不同品牌的交换机上,每一个端口都会有一个缺省的 VLAN ID
- 思科交换机上称为 Native VLAN,即本征 VLAN
- 华为交换机上称为 Port VLAN ID,即端口 VLAN ID,简记为 PVID
Access
在 Access 端口中,一般只用于连接用户计算机,且它只支持 “未打标签” 的普通以太网 MAC 帧,收到帧后再给它打标签,确认要转发目标也是 Access 端口的主机时,则去标签转发。
- Access 端口只能属于一个 VLAN(一个端口不能同时属于两个 VLAN)
- Access 端口的 PVID 值和端口所属的 VLAN ID 相同
当我们划分出两个 VLAN,并将不同的 Access 端口添加到对应 VLAN 中后,发送帧时,交换机会添加 VLAN 标记字段,再判断它属于哪一个 VLAN,比如右侧 C 主机发送广播帧后,交换机加上 VLAN 字段后,发现它是属于 VLAN3 的,于是只会在 VLAN3 中对这个帧进行广播转发,不会影响 VLAN2 里面的设备。
Trunk
Trunk 端口一般用于两个交换机的连接(一个交换机接口不够用了),它可以属于多个 VLAN,并供用户设置 VLAN ID 的值;
- 下图所示,我们将 Trunk 端口的 PVID 设置为 1,假设 A 发送帧,因为从 Access 端口接受,交换机 1 会对其加上 VLAN ID 字段。并检测当前交换机中有哪些端口的 PVID 是 1
- 因为 2 号 Access 端口和 5 号 Trunk 端口的 PVID 都为 1,交换机 1 将该帧去端口转发给主机 B 和 Trunk 端口
- 交换机 2 从 5 号 Trunk 端口收到该帧,因为它内部没有打标签,所以它知道该帧的 PVID 和 Trunk 端口的 PVID 相同,将其打上 VLAN1 的 ID,并检测交换机器 2 中那些端口的 PVID 为 1,将其去标签转发给主机 E 和 F
如果是 C 主机发送广播帧,此时该帧的 PVID 和 5 号 Trunk 端口的 PVID 不符合,交换机 1 会将该帧不去标签从 Trunk 端口转发出去(即不管 Trunk 端口的 PVID 是多少,交换机都需要将报文发给他,只不过发送的内容是否有标签的区别)
交换机 2 收到这个带 VLAN2 标签的帧后,就会直接检测那些端口的 PVID 为 2,将其去标签转发给对应主机。
下面是一个练习题
答案如下,需要注意两个交换机互联时,Trunk 端口的 PVID 必须要设置为相同的值,否则会出现错误转发的情况。
Hybrid(华为交换机私有)
Hybrid 端口可以单独设置需要对那些 PVID 进行去标签操作,当收到帧的 PVID 不在去标签设置中时,会直接发送给主机,此时主机收到一个不支持的 IEEE 802.1Q
帧,会将其丢弃!
而如果一个帧的 PVID 在去标签的设置中,则去标签后再发送给主机。此时主机就能收到一个正常的 MAC 帧,从而进行处理。
通过给不同端口设置去标签 VID 值,其实就是变相的设置了该端口能属于那些 VLAN,从而受到不同 VLAN 里面的帧,设置比固定的单个 VLAN 更加灵活!
总结
数据链路层
- 数据链路层的作用:两个设备 (同一种数据链路节点) 之间进行传递数据;
- 以太网是一种技术标准;既包含了数据链路层的内容, 也包含了一些物理层的内容。 例如:规定了网络拓扑结构,访问控制方式,传输速率等;
- 以太网帧格式
- 理解 mac 地址
- 理解 arp 协议
- 理解 MTU
网络层
- 网络层的作用:在复杂的网络环境中确定一个合适的路径.
- 理解 IP 地址,理解 IP 地址和 MAC 地址的区别
- 理解 IP 协议格式
- 了解网段划分方法
- 理解如何解决 IP 数目不足的问题,掌握网段划分的两种方案
- 理解私有 IP 和公网 IP
- 理解网络层的 IP 地址路由过程
- 理解一个数据包如何跨越网段到达最终目的地
- 理解 IP 数据包分包的原因
- 了解 NAT 设备的工作原理
传输层
- 传输层的作用:负责数据能够从发送端传输接收端
- 理解端口号的概念
- 认识 UDP 协议,了解 UDP 协议的特点。
- 认识 TCP 协议,理解 TCP 协议的可靠性
- 理解 TCP 协议的状态转化
- 掌握 TCP 的连接管理,确认应答,超时重传,滑动窗口,流量控制,拥塞控制,延迟应答,捎带应答特性
- 理解 TCP 面向字节流,理解粘包问题和解决方案
- 能够基于 UDP 实现可靠传输
- 理解 MTU 对 UDP/TCP 的影响
应用层
- 应用层的作用:满足我们日常需求的网络程序,都是在应用层
- 能够根据自己的需求,设计应用层协议
- 了解 HTTP 协议
- 理解 DNS 的原理和工作流程
- 最新
- 最热
- 最早
- 作者
点击重新获取 | 打开控制台