IP协议详解

1.概念

1.1 四层模型

  • 应用层:解决如何传输数据(依照什么格式/协议处理数据)的问题
  • 传输层:解决可靠性问题
  • 网络层:数据往哪里传,怎么找到目标主机
  • 数据链路层(物理层):数据在物理硬件层面上传输

网络层的IP协议,就是让网络,拥有将数据从A主机发送到B主机的能力。

注意:拥有此能力,并不代表每次都能成功送达!(即可靠性非100%)

而失败的时候,就需要传输层来获取发生错误的原因,并进行错误重传或者超时重传等操作。

所以,在从网络层获取到信息成功送达到对方的反馈之前,传输层需要暂时保留已经发送的数据。如果网络层发送失败了,传输层需要进行重发操作,以保证可靠性。

1.2 主机、路由器、节点

  • 主机:拥有IP地址,但不进行路由控制的设备
  • 路由器:拥有IP地址,并进行路由控制的设备
  • 节点:主机和路由器的统称

2.IP报文

2.1 报文结构

IP协议的报文与TCP的报文有一定程度上的相似

下图为IPV4中报文的格式(IPV6的报文和下图是不同的)

image-20230724122812011

对图中首部字段的解析如下:

  • 四位版本号:指定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报头中,如下标识、标志、片偏移字段就是用于分片和组装操作的

image-20230724185036181

  • 十六位标识 (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,这时候就根据片偏移排序进行拼接就可以了。(这只是个栗子)

image.png

如果对第二个分片还需要进一步分片,效果如下

image.png

分片练习题

下面是一个分片的例题,包含了偏移量的选择问题;

可以看到,一开始我们选择将原有的数据载荷拆成780+700的两部分,但通过计算得出,780/8=97.5,并非8的整数倍,此时第二个IP报文中首部的偏移量字段就不符合要求了,所以我们要保证第一个报文中的数据载荷的长度是8的整数倍。

image.png

此时要采用向下取整的策略,即780/8=97.5,则我们采用97*8=776为第一个IP报文的数据载荷长度,第二个报文的数据载荷长度为1480-776 = 704,因为第二个报文是最后一个分片,所以它的数据载荷部分长度没有8的倍数的要求。

image.png

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
2
3
20+1480
20+1480
20+20

一共需要分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来控制生命周期,报文就会一直在这个死循环中跑,白白浪费路由器的性能!

image-20230726201220852

规定了TTL之后,当报文的生命周期已经到了,但却还没有发送到目的地,那就需要将这个报文丢弃了。即超了TTL的时间就认为报文无效;

  • 最初是以IP数据报中该字段的值减去报文在当前主机耗费的时间,若结果不为0则转发,否则丢弃;
  • 现在以跳数为单位,路由器转发IP数据报的时候,将该字段值减一,如果减一后不为0则转发,否则丢弃

image.png

2.4 协议字段

协议字段占8比特,标识IPV4报文携带的是什么协议数据单元。

1
2
3
4
5
6
ICMP 1
IGMP 2
TCP 6
UDP 17
IPv6 41
OSPF 89

image.png

为什么协议字段里面会有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地址。

image.png

除了读取目的IP地址,题目还需要我们读取默认网关的MAC地址。而主机的默认网关一般都是路由器,该题转为读取以太网帧格式中的目的MAC地址

image.png

第四题,该报文中IP分组经过路由器R转发的时候,应该需要修改源IP地址、生存时间、首部校验和字段。

如果是在不同的子网中进行转发,源IP地址不需要修改。如果是通过路由器的WAN口转发到上层局域网,则需要修改源IP地址。

如果路由器所连的两个局域网中MTU不同,可能需要对IP重新进行分片。

3.网段划分

3.1 IP如何找到对方主机?

IP如何找到对方主机呢?

  • IP = 目的网络 + 目的主机
  • 由此还衍生出网段划分

以IPV4的ip为例,其格式为1.1.1.1,可以认为是下面的划分

1
2
1.1.1  .1
网段 .主机

这就好比你的学号,前X位里面是学院的代码,最后才是班级+班级内编号。先找到你所在学院,再找到班级,最后再找到你。

反馈到IP里面,就是先找到网段,再找到主机。

3.1.1 发送数据的本质

IP就是先找到目的的网段,再找这个网段中的目的主机。(先根据目的网段进行路由,找到目的网络,再通过主机号找到目的主机)

而查找目的主机的过程,本质是一个排除的过程。

先通过网段排除一个大类,再通过主机号来排除该网段中的单个主机。这样就能避免我们一个一个遍历在全网中查找主机,提高了查找的效率

子网划分的目的:就是提高查找目标主机的效率

这也是学校里面用学号的原因,除了为了给每个学生提供一个唯一标识,还能通过学号来提高查找到某一个学生的效率。

在全球互联网上,同样是通过IP地址的网段来划分国家,再划分到每个国家内部的不同区。这时候就会有一定IP地址资源的竞争。比如米国互联网发展早,下图中谷歌的服务器IP就老整齐了😂(不过这些服务器都在同一个地域,IP很接近是合理的)

image-20230726132920408

3.1.2 网络号和主机号

  • 网络号:保证相互连接的两个网段具有不同的标识
  • 主机号:在同一网段中,主机之间具有相同的网络号,但是主机号不同以进行区分

具体框架可以查看下图,192.168.128就是这个局域网的网段 ,而最后的10和11是不同主机的两个主机标识。一般情况下,网段中的1号主机就是这个网段中的路由器。

我们家里的路由器除了进行路由转发,还有子网划分的功能。

如果出现了一个开头并非192.168.128的IP,主机就能知道这不是当前局域网的IP,于是就会将报文直接转发给192.168.128.1,让路由器去找这个IP的目标主机(进行跨局域网的下一层转发)

img

就好比我们的学号是学校的教务系统派发给每一位同学的,IP中的网段也被“某人”在一定程度上根据地区进行了划分。

3.2 IP地址类别划分

3.2.1 ABCDE

通过A到E类不同的划分,会衍生出不同范围的IP号,然后再分配给不同的国家或地区

1
2
3
4
5
A类 0.0.0.0到127.255.255.255 
B类 128.0.0.0到191.255.255.255
C类 192.0.0.0到223.255.255.255
D类 224.0.0.0到239.255.255.255
E类 240.0.0.0到247.255.255.255

image-20230726110410094

依照上面的划分,如果我是一个大型企业,申请了一个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的类别划分方式了,都采用了子网掩码方式。

image-20230726133558191

根据上图可见,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地址的判断

img

在云服务器上执行ifconfig,也能看到本地环回的配置项;这里我们能发现,本地环回的MTU是远高于网络的1500。毕竟是自己和自己通信,基本不会出现丢包,传输速度也是飞快,也就不用担心数据包太大的问题

同时也能发现,我们的云服务器被分配到的ip地址并不是云服务器的公网ip,这也就表明了我们的云服务器并没有被直接暴露在公网上,而是通过了云服务器厂家的入网服务器(或者也叫路由器)来进行公网ip的映射和数据包的转发操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
inet 10.0.12.2 netmask 255.255.252.0 broadcast 10.0.15.255
inet6 fe80::5054:ff:fec9:274f prefixlen 64 scopeid 0x20<link>
ether 52:54:00:c9:27:4f txqueuelen 1000 (Ethernet)
RX packets 277674393 bytes 80031748700 (74.5 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 302405663 bytes 162670581730 (151.4 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 111135687 bytes 27644436547 (25.7 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 111135687 bytes 27644436547 (25.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

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访问的报文并不会被转发到上层路由器上,也就不可能凭空飞到其他局域网中

image-20230726185645407

图中的 122.77.241.3 就是一个公网IP的服务器,当我们需要访问这个主机的时候,局域网的家用路由器在检测到这个目的IP的时候,发现其并不是局域网的IP地址,于是就会将这个报文给转发给上层的运营商路由器。

运营商路由器是直接接入了公网IP的,其就能通过网段划分+主机编号来查找目标主机,将报文转发给 122.77.241.3 服务器,再将服务器返回的信息转发给你的家用路由器,再转发到你的主机上。

这也告诉我们,想绕过运营商直接获得公网IP是不可能的,因为从物理层面上,我们的家用路由器就不是接在公网IP上的!即便是可以申请到的家用公网IP,也和云服务器的入网服务器一样,是运营商的路由器分配给你的。

你会发现,家用申请的公网ip,很多端口都是被屏蔽的(比如 80/443/8080)这些端口的屏蔽操作,以及海外网站的「墙」也是运营商的路由器进行检测和屏蔽的!

4.3.2 内网访问公网的流程

假设我们的模型如下

1
2
3
4
5
6
7
广域网

运营商路由器C (公网IP是122.77.241.4)

家用路由器B (运营商路由器分配私有IP 10.1.1.2)

家用主机A (家用路由器分配私有IP 192.168.1.201)

下面是一个家用主机A,访问公网IP的主机 122.77.241.10 的具体步骤;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
本地主机A发送IP报文给家庭路由器B
————————————————————————
| 源IP:192.168.1.201 |
| 目的IP:122.77.241.10 |
————————————————————————

路由器B收到报文后,检测目的IP,发现并不似局域网的IP
于是交付给上层的运营商路由器C;
报文的源IP被修改为家用路由器B的wan口IP
————————————————————————
| 源IP:10.1.1.2 |
| 目的IP:122.77.241.10 |
————————————————————————

运营商路由器C收到报文后,发现其也不是自己所在内网 10.1.1.* 的局域网IP
于是开始执行广域网IP寻址操作,找到目标公网IP的主机,再将报文发送给该主机;
此时发送的报文又被改成了
————————————————————————
| 源IP:122.77.241.4 |
| 目的IP:122.77.241.10 |
————————————————————————

4.3.3 NAT技术

这种不断替换源IP来进行路由转发的过程,就是NAT技术

也正是NAT技术的存在,让我们能通过多级局域网来让更多的设备上网,大大缓解了公网IP的不足。

也正是因为IPV4地址不足的问题被大大缓解,推广IPV6就没有我们想象中的那么顺利了。毕竟IPV6的IP格式和V4完全不同,需要每个层级的路由器都进行功能升级,这可是一个巨大的工程!

image-20230822092804992

4.3.4 NAT怎么回来?NAPT

当目标主机收到这个报文后,他的反馈报文如下

1
2
3
4
————————————————————————
| 源IP:122.77.241.10 |
| 目的IP:122.77.241.4 |
————————————————————————

同样是先到达运营商路由器,运营商路由器需要缓存每一个转发到公网的报文的来源信息;为此路由器会维护一个转换表,记录着局域网主机的私有IP地址:端口号与对应的公网IP地址:NAT端口号的映射关系。

比如此次TCP链接中,我将路由器公网IP的122.77.241.4:30000映射给了局域网10.1.1.2:40000;当从公网收到服务器的响应报文后,从转换表里面就能够查到这个映射(一次通信中这个NAT映射是不会变的)从而确定该报文的局域网流向。

需要注意的是,NAT技术在端口映射的时候不一定会映射到和内网主机相同的端口,此时不仅需要修改IP报文中的来源IP,还需要进一步修改传输层(比如TCP和UDP)中的源端口号

确定局域网IP后,就修改当前报文的目的IP,继续往局域网转发;后面的子路由器也是如此,不再赘述。

1
2
3
4
————————————————————————
| 源IP:122.77.241.10 |
| 目的IP:10.1.1.2 |
————————————————————————

这种IP:端口的关联关系表,就是由支持NAT技术的路由器来维护的,这个转换表被称为NAPT;当这次链接结束后,这对映射关系就会从转换表中被删除。

image-20230822093121691

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 正向代理

正向代理是位于客户端和目标服务器之间的中间服务器。客户端通过正向代理来访问互联网上的资源,而不是直接连接到目标服务器。正向代理的作用主要有以下几点:

  1. 访问控制和过滤: 正向代理可以用于限制用户访问特定网站或资源,以实施访问控制策略。它可以过滤不良内容,提供更好的安全性和隐私保护。
  2. 隐藏客户端身份: 正向代理可以隐藏客户端的真实 IP 地址,从而保护用户的隐私和匿名性。目标服务器只能看到代理服务器的 IP 地址,而无法获取到真正的客户端信息。
  3. 缓存和加速: 正向代理服务器可以缓存常用的内容,从而减少对目标服务器的请求,提高访问速度。
  4. 突破防火墙限制: 在一些网络环境中,访问特定网站可能受到限制或封锁。使用正向代理可以绕过这些限制,访问被封锁的资源。

4.4 ISP检测宽带账户

下文中的部分内容来自chatgpt,我对里面的内容进行了补充和修改

运营商的路由器还会检测我们的账户是否还有余额。我们的家用路由器一般是通过光猫登录了自己的宽带账户;也可以将光猫设置成桥接模式,将接入光猫的路由器设置为宽带帐号(PPPoE)上网方式,登录运营商提供的宽带账户和密码,来接入网络。

ISP的路由器检测你的宽带账户通常是通过以下步骤来完成的:

  1. 设备连接与识别: 当你的计算机或其他网络设备连接到ISP提供的路由器时,路由器会分配一个私有IP地址给你的设备。这个IP地址是由路由器动态分配的,并且通常处于特定的私有IP地址范围,比如10.11.1.0/1610.11.1.0/24等。此时,你的设备与路由器建立了一个大局域网内的连接。(运营商并不会直接给你分配192.168.*这样的私有IP,因为这个IP一般是用于最底层局域网的,给你分配了,那家庭局域网的就没IP用了)
  2. 认证过程: 在你尝试访问互联网时,ISP的路由器会引导你的设备进行认证过程。这通常涉及向ISP服务器发送你的宽带账户的登录凭据(例如,用户名和密码)。
  3. 账户验证: ISP的服务器会验证你提供的登录凭据是否与其记录中的账户匹配。如果验证成功,说明你的宽带账户是有效的,路由器会为你的设备分配一个公共IP地址,这个IP地址是可供互联网访问的。
  4. IP地址分配: 一旦认证成功,ISP的路由器会为你的设备分配一个公共IP地址。这个IP地址是在互联网上唯一标识你的设备的地址,使你的设备可以与其他互联网设备进行通信。
  5. 数据传输: 一旦有了公共IP地址,你的设备就可以与互联网上的其他设备进行通信,发送和接收数据。

这样,通过认证和IP地址分配的过程,ISP的路由器可以检测并识别你的宽带账户,从而让你的设备能够访问互联网。

人话就是,登录了宽带账户以后,当我们路由器发送的报文交付到运营商路由器的时候,该路由器就会检测你这个宽带账户的余额。如果没有余额了,就会直接丢弃掉你的IP报文。我们看到的结果就是无法上网!

这个操作并不是每次都会执行的,只要你的路由器能稳定的接入到运营商的路由器上,那就不会每次都进行宽带账户的验证,否则会增加网络的负担。

具体的验证流程都是运营商路由器和你的本地光猫自动完成的。

而手机没有话费余额的时候,我们依旧能拨通诸如120、119等紧急号码,这也是运营商的服务对这些特殊的电话号码做了类似于免费白名单的操作(手机号码可以类比公网IP来理解)

5.域名

5.1 DNS服务器

上文讲述的都是关于IP协议的事情。但实际上我们日常生活中,一般都不会直接使用IP地址+端口号的方式来访问某一个服务,而是使用域名来访问。比如

1
2
www.baidu.com
www.google.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
2
8.8.8.8 谷歌公司的DNS服务器
119.29.29.29 腾讯提供的公共DNS

先请求这些DNS服务器,服务器内会针对域名查询对应的DNS解析,最后再访问该解析对应的IP地址

而使用域名的时候,默认访问的是该主机的 80(HTTP)/443(HTTPS) 端口 ,我们也可以像IP一样,在域名之后用:端口 来指定特定端口号进行访问,即域名:端口

一般情况下,我们的主机都可以通过自动配置DNS从上层路由器中获取到DNS服务器的地址(比如运营商会在路由器基站中内置DNS服务器)

5.2.1 DNS劫持

所谓DNS污染和DNS劫持,就是因为我们访问的DNS服务器的时候,获取到的结果和预期不同,从而导致无法访问目标网站,或者访问了的目标网站

1
2
www.baidu.com 明明应该指向 1.1.1.1
但被坏蛋劫持了DNS解析,变成了指向 1.1.1.3

坏蛋可以在 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解析,而是通过层级结构来分散和管理这些信息。

img

5.4 dig工具分析DNS解析过程

1
yum install bind-utils

安装了之后就可以使用dig命令来查看域名解析过程了

1
dig 域名

6.路由

路由的过程,就是下图这样一跳一跳(Hop by Hop) “问路” 的过程

所谓 “一跳” 就是数据链路层中的一个区间。具体在以太网中,指从源MAC地址到目的MAC地址之间的帧传输区间。

image-20230726194559495

6.1 问路栗子(帮助理解)

拿日常生活中问路来举例子(请屏蔽现在有导航这件事)一般问路,会得到三种结果:

  • 我不知道
  • 我知道XXX知道
  • 我不知道,但是给你乱指(在路由寻址的过程中,这个情况是不存在的)

比如张三要去南京大学的仙林校区,他飞机落地南京后,不知道怎么走;他找了个机场的保安,问他“我是从xx省新来的大学生,应该怎么去南京大学?” 保安让他先坐xx路公交车去仙林大学城,到哪里再去问其他人。

张三到了仙林大学城后,又找到了路边的环卫工,又问“我从机场过来,应该怎么去南京大学。” 环卫工给他指了条明路,那里就是南京大学的教学楼,入口就在这附近。这时候张三获取了两个人(路由器)的帮助,成功递达了南京大学的仙林校区(目标主机)。

可以看到,问路和上面图示中IP报文的路由是很相似的,都是一个路由一个路由的“问路”,最终找到目标主机。


6.2 路由表

报文在各个路由器之间路由也是如此。当一个路由器遇到一个IP报文

  • 他首先检测这是不是自己分配的局域网中的IP,是则可以直接转发到目标主机;
  • 不是则查看自己的路由表,看看自己是否保存了这个IP应该往哪里走;
  • 如果它自己不知道,则可以和其他与自己相连的路由器通信(信息同步),问他们这个IP应该给谁(就好比你在公司里面问某一个工作应该交给谁去做)
  • 如果周边的路由(旁边的路人)都不知道该往哪里去,则路由器会将其交付给自己的默认路由,报文继续转发,去下一个人那里问路了。

这样的跳跃都会有一个前提条件:相邻的两个主机(或路由器)一定是在物理上相连,处于同一局域网之中。每次的跳跃,本质上是从一个子网跳到另外一个子网;广域网可以认为是最大的“子网”

image-20230727085331798

6.3 route命令

在windows电脑上可以使用 route print 命令打印路由表,本文不关注windows平台。

在linux平台里面,可以通过route命令查看当前主机的路由表,在Centos8主机上,命令显示如下。

可以看到,路由表的名字叫做 内核IP路由表,这也是linux系统中的一个内核数据结构。内部维护了路由表的目的地、Gateway网关、Genmask掩码、Flags标志位、Metric、Ref、Use、Iface接口

1
2
3
4
5
6
7
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default _gateway 0.0.0.0 UG 100 0 0 eth0
172.16.0.0 0.0.0.0 255.255.0.0 U 100 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-96d69eeef1ac
172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-fa4aff4c583e
  • 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
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
#include <iostream>
#include <arpa/inet.h>

int main() {
// 将点分十进制的IP地址和子网掩码转换为无符号整数
unsigned int ipAddress = inet_addr("172.16.0.2");
unsigned int subnetMask = inet_addr("255.255.0.0");
// 进行按位与操作得到网络地址
unsigned int networkAddress = ipAddress & subnetMask;
// 将网络地址转换回点分十进制表示法并输出结果
struct in_addr addr;
addr.s_addr = networkAddress;
std::cout << "IP地址: " << "172.16.0.2" << std::endl;
std::cout << "子网掩码: " << "255.255.0.0" << std::endl;
std::cout << "网络地址: " << inet_ntoa(addr) << std::endl;

return 0;
}
// 非linux环境可以使用下方的代码进行处理
void ip_and_gmask_test() {
unsigned int ipAddress = 172 << 24 | 16 << 16 | 0 << 8 | 2; // 将 IP 地址转换为 32 位无符号整数
unsigned int subnetMask = 255 << 24 | 255 << 16 | 0 << 8 | 0; // 将子网掩码转换为 32 位无符号整数

unsigned int networkAddress = ipAddress & subnetMask; // 按位与

std::cout << "IP地址: 172.16.0.2" << std::endl;
std::cout << "子网掩码: 255.255.0.0" << std::endl;
std::cout << "网络地址: " << (networkAddress >> 24) << "." << ((networkAddress >> 16) & 255) << "." << ((networkAddress >> 8) & 255) << "." << (networkAddress & 255) << std::endl;
}

二者输出结果都是172.16.0.0

得到输出结果后,再和路由表中该项的Destination进行对比,二者相等,代表当前IP就是需要通过该项进行路由,那就将这个报文通过Iface接口eth0发送出去就OK了

1
2
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.0.0 0.0.0.0 255.255.0.0 U 100 0 0 eth0

如果按位与的结果与Destination匹配不上,那就继续往下一个条目的子网掩码进行按位与。如果整个表都按位与完毕了,还没找到该去的地方,那就将其通过default默认路由发送出去。

7.数据链路层

前面谈了一大堆将数据从一个主机到路由器再跨越多个路由器递达目标主机的流程。

要想实现这一点,我们还需要数据链路层的帮助,即实现同一局域网内两台主机在物理层面上的相互通信。

  • IP网络层:提供跨网络找到正确路由路径,并传输数据的能力
  • 数据链路层:提供将数据在同一网络中传输的能力
  • 物理层:实际上的数据在物理(网线)上传输

数据链路层也有不同的传输方式,本文主要关注当前主流的以太网

需要注意的是,以太网不是一种具体的网络,而是一种技术标准。它即包含了数据链路层的内容,也包含了一些物理层的规定,比如拓扑结构和访问控制方式,传输速率等。

注意:本文中关于数据链路层的知识并不完整,因为当时的学习目标是最常用、面试最常考的网络知识,并没有完整学习计算机网络。

2023-12-17:后续计算机网络的完整总结会根据B站视频的学习单独开一个博客。

7.1 认识局域网的基本情况

7.1.1 局域网认识

不过在认识以太网之前,我们需要先知道局域网通信的一个基本情况;假设下图中就是一个局域网,其中包含了不同的主机

image-20230821171017532

首先,如果想要两台主机能够通信,我们就需要先将其链接到同一根网线上(在这里暂且不管WIFI,其实本质上也是连在了这根网线上);这就好比进程间通信的时候,你需要先能看到同一份资源,才能实现对这份资源的共享访问。

虽然我们认为在局域网里面通信的时候,是两台主机直接交流,但实际上你可以把局域网当作一个教师,当张三和李四沟通的时候,其实会有很多其他的吃瓜群众都能接收到你发送的这个信息。

反馈到以太网报文上,因为每个主机都是知道自己的地址的,所以只要检测到以太网报头中的目的地址不是自己的时候,就可以丢弃掉这个报文。因为目标并不在和你说话。这就好比在教室里面,你隔壁有俩人在聊天,他们并没有和你交谈,所以你可以不用管他们聊了什么。

这也反馈出了为什么某些公共WIFI会不安全,因为只要接入了这个WIFI,你就有办法检测到其他用户通过这个局域网发送了什么信息!

因为局域网内的主机是通过目的地址判断有没有人和自己聊天的,所以即便我们的电脑开机后什么事情都没有干,在操作系统底层(数据链路层)其实一直都在从局域网中拿到新的数据链路层报文,并检测是否是发给自己的报文:

  • 是,向上交付
  • 否,丢掉

7.1.2 碰撞域

由于数据链路层向下是直接交付给物理层的,在物理层(网线)中光电信号传输是不能同时传输多个数据的,这就要求我们同一个局域网的多台主机不能同时往局域网中发数据。为了解决这个问题,主机引入了休眠机制,通过不同时间的错开休眠,来避免两台主机同时往局域网中发数据的情况。

  • 理想情况:同一时间只有一台主机在局域网中发送数据
  • 碰撞问题:如果出现了同时发送,数据出现冲突,就需要剔除掉这部分数据

通过碰撞域解决数据冲突问题,尽量达到理想情况;

比如我们的交换机就有划分碰撞域的功能。接到交换机上的设备,除了通过交换机进行路由转发,如果在交换机的这部分设备中出现了数据碰撞,那么交换机就能把碰撞控制在当前这个小的碰撞域内,而不会向更大的局域网中传播。

如果一个局域网里面只有一个交换机(路由器)那么这整个局域网共享碰撞域

所以大公司内为了避免局域网因为碰撞问题而导致的网络卡顿,一般都会将几台电脑接入一个小的交换机中来划分碰撞域

如上是物理层面的事情,软件层面上,一个MAC帧不要太大,否则会大大增加碰撞的概率。所以MAC帧必须要对上层交付的数据大小提一个要求,不能交付太大的数据,这就是MTU的由来(一般都是1500字节,至于为什么是1500,那就是学术层面的事情了)

7.1.3 链路和数据链路

链路是一条物理的链路,中间没有任何交换节点;

数据链路是基于链路上的,需要有协议来实现数据的传输;将这些协议和硬件加入到链路上,就行程了数据链路;

7.2 以太网帧格式(MAC帧)

image-20230803144819270

7.2.1 MAC帧格式

这部分的知识可以小结如下

image-20231217102831227

以太网MAC帧格式分为下面两种,这两种格式仅类型字段有区别

  • DIX Ethernet V2(流行,后文涉及到的是这个)
  • IEEE 802.3

其中以太网V2的MAC帧格式如下

image-20231217104707151

  • 目的地址(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

请注意,数据链路层的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;

image-20231217102953795

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)

image-20230821185112918

7.4.4 MSS

除了MTU,还有另外一个概念是MSS,既应用层中有效数据部分的长度

  • MAC帧中有效数据长度是[46,1500]

image-20230821202306869

image-20231121221114046

7.5 ARP协议

ARP协议属于数据链路层,可以认为是MAC帧协议的“上层”

image.png

7.5.1 说明

因为在局域网内的传输时,我们是用MAC地址来作为不同主机的标识符的,所以就必须存在一个IP地址到MAC地址的转换。

ARP协议也是包含在以太网帧格式这个整体概念中的,其中属于ARP自己的正文只有28个字节;因为MTU限制最小的数据长度是46字节,所以在发送ARP报文的时候,需要给这个28字节后面填补空位。

image-20230821192339835

先来看看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资源浪费

image-20231217103821673

如果局域网内不会频繁出现广播信息,这个问题还不是很严重。但是当前使用的很多协议都会发出广播,网络中会频繁出现广播信息,这就很难受了。

image-20231217105549873

为了解决这个问题,引入了虚拟局域网VLAN技术,来将一个大的广播域划分为各个小的广播域。

除了使用虚拟局域网,还可以使用路由器来隔离广播域。但是路由器成本较高,虚拟局域网只需要在支持的交换机中进行设置即可,成本较低。

7.6.2 虚拟局域网VLAN

虚拟机局域网 Virtul Local Area Network 是一种将局域网内设备化为与物理位置无关的逻辑组的技术,让这些逻辑组成为一个独立的逻辑上的局域网。

如下图,使用VLAN1和VLAN2对设备进行隔离后,只有同一个VLAN里面的设备才能相互通信,不同VLAN直接不会收到相互的广播域。

image-20231217110239038

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

注意,这个操作是由交换机来实现的,而不是用户主机实现的。

image-20231217110509950

交换机端口类型

交换机的端口类型有下面三种

  • 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相同

image-20231217111245910

当我们划分出两个VLAN,并将不同的Access端口添加到对应VLAN中后,发送帧时,交换机会添加VLAN标记字段,再判断它属于哪一个VLAN,比如右侧C主机发送广播帧后,交换机加上VLAN字段后,发现它是属于VLAN3的,于是只会在VLAN3中对这个帧进行广播转发,不会影响VLAN2里面的设备。

image-20231217111421321

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

image-20231217111927004

如果是C主机发送广播帧,此时该帧的PVID和5号Trunk端口的PVID不符合,交换机1会将该帧不去标签从Trunk端口转发出去(即不管Trunk端口的PVID是多少,交换机都需要将报文发给他,只不过发送的内容是否有标签的区别)

交换机2收到这个带VLAN2标签的帧后,就会直接检测那些端口的PVID为2,将其去标签转发给对应主机。

image-20231217112451365

下面是一个练习题

image-20231217112554010

答案如下,需要注意两个交换机互联时,Trunk端口的PVID必须要设置为相同的值,否则会出现错误转发的情况。

image-20231217112627996

Hybrid(华为交换机私有)

Hybrid端口可以单独设置需要对那些PVID进行去标签操作,当收到帧的PVID不在去标签设置中时,会直接发送给主机,此时主机收到一个不支持的IEEE 802.1Q帧,会将其丢弃!

而如果一个帧的PVID在去标签的设置中,则去标签后再发送给主机。此时主机就能收到一个正常的MAC帧,从而进行处理。

通过给不同端口设置去标签VID值,其实就是变相的设置了该端口能属于那些VLAN,从而受到不同VLAN里面的帧,设置比固定的单个VLAN更加灵活!

image-20231217112923088

总结

数据链路层

  • 数据链路层的作用:两个设备(同一种数据链路节点)之间进行传递数据;
  • 以太网是一种技术标准;既包含了数据链路层的内容, 也包含了一些物理层的内容。 例如: 规定了网络拓扑结构, 访问控制方式, 传输速率等;
  • 以太网帧格式
  • 理解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的原理和工作流程