交换机与路由
前言
在我们初学计算机网络的时候, 在没有基础情况下一定会遇到一些比较抽象的名词概念, 如:
-
交换机看MAC
-
路由器看IP
-
传输层与端口
-
7层OSI模型
-
网络报文分层
如果你对其感到困扰, 或难以理解, 其实不妨剥离抽象, 重新回归计算机数据的本质: 二进制字节流 .
以最原始的视角, 重新看待这些抽象后的内容.
理解偏移与数据分层
就和我们平时写代码一样,为了归纳一段结构相同的数据, 就会使用一种结构将其组合起来, 达到可复用的目的.
typedef struct Book {
int id;
char title[50];
char *body;
} __attribute__((packed)) Book; // 取消字节对齐
这样做的好处是, 我们提取或填充其中的内容的时候就非常直观.
Book mybook;
mybook.id = 1;
strcpy(mybook.title, "MyBook");
mybook.body = "... content ..."
但究其本质, struct 在内存中存储的不过是一段连续的字节, 所谓的结构体成员不过是, 编译器帮助你维护的一张由字段名到偏移量的映射表.
我们使用下标+偏移量, 依然可以做到相同的效果.
unsigned char *raw = (unsigned char *)&mybook;
*(int *)(raw + 0 ) = 1;
strcpy((char *)(raw + 4 ), "MyBook");
*(char **)(raw + 54) = "... content ...";
清楚了这一点之后, 再回头看网络协议, 其原理就不难理解了.
无非就是将一段 uint8_t buff[ ] , 按照约定好的方式, 进行切割成不同的字段, 由不同的硬件设备或程序进行解析.
以太网帧
为了更好的理解以太网帧的构成, 我这里制作了一张表格.
它以一个内网 IP 发起通信为例,直观展示了一张内网通信报文是如何构成, 又如何被分层逐步解析的. ( 这里只列举了必要的最基础转发 )
后续的讲解都将依照这个表格.
第一层 ( 物理层 )
通过网线、光纤或无线信号等物理介质,把封装好的数据转换为可通过硬件设备传播的电信号或电磁波。
在这一层级, PC的操作系统会将, 发送的数据包, 以表格内的格式进行封装, 发给下一层级.
MAC
此时的以太网协议头中的 源MAC 地址为 PC 机自身的MAC.
而 目标MAC 则根据 目标IP 进行判断, 分为内网和外网两种情况:
-
内网:
若 ARP 缓存中存在目标IP的MAC, 则直接写入.
若不存在则通过交换机发送一条 ARP 广播帧, 向同一网段内设备广播查询, 等待目标设备回应. 收到回应后写入封装; 若多次尝试仍无应答, 则封装失败并返回发送超时.
-
外网:
直接将 网关MAC 写入封装.
-
注意:
- 大部分交换机不负责代替终端应答 ARP; 即便交换机已经学习到 MAC 与端口的映射关系, 也不会直接替目标主机回应 PC.
- 因为以太网帧进行封装都是 先 ARP 后 数据, 因此在无 ip 应答时, 返回的不是丢包, 而是 发送超时.
IP
此时以太网协议头中的 源IP 为 PC自身的内网ip, 目标IP 为最终目的地的IP.
Type
对应的以太网类型, 08 00 为 IPv4 , 86 DD 则为 IPv6.
第二层 ( 数据链路层 )
通常是交换机完成这一层, 通过读取以太网帧头中的 两组 MAC 地址, 将 以太网帧 发送给 目标MAC.
- 交换机收到数据帧后,先读取 源MAC,学习该 MAC 当前的 入端口,并写入 MAC 地址表.
- 然后读取 目标MAC,查询 MAC 地址表.
- 如果查到了目标 MAC 对应的端口,就将数据帧单播转发到该端口.
- 如果没有查到目标 MAC 对应的端口,就会在同一 网段内 除 入端口 外进行泛洪,等待目标设备后续回包,从而完成 MAC 学习.
- 交换机中的 MAC 地址表不是永久保存的。每条记录都有老化时间,如果在一段时间内没有再次看到该 MAC 发来的帧,这条表项就会被交换机自动清除,这个过程称为老化。
- 在实际网络中,这种未知单播泛洪通常不多见,因为在前面的 ARP 请求和 ARP 应答过程中,交换机往往已经学到了 PC 与目标设备(或网关) 两端的 MAC 与端口映射关系。
- 这里的提到的端口, 并非TCP端口, 而是指的网线链接的物理端口.
总的来说, 交换机(第二层设备)并不关心包里的 IP 是多少, 也不关心应用层发的是网页还是聊天消息, 它只负责在当前链路范围内, 按照 MAC 地址把帧送到下一跳设备.
第三层 ( 网络层 )
通常是路由器完成这一层, 通过读取 IPv4 协议头中的 目标IP 字段, 来决定这个数据包应该被发到哪个网络.
- 路由器收到交换机转发过来的数据帧后, 会先去掉第二层的以太网帧头, 然后取出里面的 IPv4 协议头.
- 读取其中的 目标IP , 并查询本机的路由表, 判断下一跳设备或出接口.
- 将 IPv4 头中的 TTL 字段减 1, 防止数据包在网络中无限循环转发.
- 再重新封装新的以太网协议头, 写入新的 源MAC 与 目标MAC, 发送给下一跳设备.
- 第三层真正关心的是 最终要去哪个网络, 而不是当前这一跳具体发给谁, 因此交换机看 MAC, 路由器看 IP.
- 在跨网段通信中, 源IP 和 目标IP 通常从发送端到接收端都不会改变, 真正会在每一跳发生变化的是以太网协议头字段中的 MAC 地址.
- 这个过程通常不只发生一次. 如果目标在外网, 数据包往往会依次经过家用路由器、运营商接入网、城域网、骨干网等多个中转设备, 每经过一跳, 都会重复一次“拆掉旧的第二层 以太网帧头 -> 读取 IP -> 查路由表 -> TTL 减 1 -> 重新封装第二层 以太网帧头”的过程.
- 另外, 虽然这一层转发时最常关注的是 目标IP 和 TTL, 但完整的 IPv4 协议头还包含版本、头长、总长度、分片、协议号、校验等字段; 图中为了突出主线只展示了核心字段.
第四层 ( 传输层 )
通常是目标主机的操作系统完成这一层, 通过读取 TCP 或 UDP 头中的端口号, 将数据交给对应的进程.
- 当数据到达目标主机后, 操作系统会先拆掉以太网帧头和IPv4协议头, 读取当前 TCP 或 UDP 协议头.
- 根据 目标端口 判断这个数据应该交给哪个应用程序, 例如 80 端口交给 Web 服务, 22 端口交给 SSH 服务
- 第四层解决的问题, 不再是哪一台主机, 而是这台主机里的哪个程序来接收这份数据.
- 也就是说, IP 负责找到主机, 端口负责找到进程, 到了这一层数据才真正开始接近具体业务.
- 同样地, 虽然第四层讲解里最直观的是“看端口分发进程”, 但完整的 TCP 头还包含序号、确认号、窗口、校验等控制信息; 图中只保留了便于理解流程的关键字段.
第五层 ( 应用层 )
这一层承载的就是最终真正有业务意义的数据内容, 例如 HTTP 请求、聊天消息、文件内容、数据库指令等.
- 当前面的第二层、第三层、第四层都解析完成后, 操作系统会把剩余的 payload 交给对应的应用程序.
- 应用程序再按照各自的协议规则, 去解释这些字节分别代表什么含义.
- 例如浏览器发送的是 HTTP 请求, 数据库客户端发送的是 SQL 指令, 聊天软件发送的是消息内容.
- 第二层决定帧先送到哪台设备, 第三层决定包最终去哪个主机, 第四层决定交给哪个进程, 第五层才决定这些数据在业务上是什么意思.
- 前面几层更像是在解决寻址、转发和传输问题, 而应用层才真正对应我们平时直接接触到的网页、消息、文件与接口请求.