网络是怎样连接的(十七)—— 生成以太网用的 MAC 头部

1,295 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情

前言

在上一篇文章中,我们介绍了 IP 头部的生成,接下来 IP 模块还需要再 IP 头部的前面加上 MAC 头部。本文我们就来探索下 MAC 头部生成的一些细节。

为什么需要 MAC 头部

首先我们来思考下我们为什么要添加 MAC 头部。

IP 头部中的接收方 IP 地址表示网络包的目的地,通过这个地址我们就可以判断要将包发到哪里。但在以太网的世界中,TCP/IP 的这个思路是行不通的。以太网在判断网络包目的地时和 TCP/IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而 MAC 头部就是干这个用的。

IP 模块在生成 IP 头部之后,会在它前面再加上 MAC 头部。MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息。

MAC 头部的结构

首先我们先来看看 MTC 头部结构,MAC 头部共 14 字节,其结构如下:

字段名称长度(比特)含义
接收方 MAC 地址48网络包接收方的 MAC 地址,在局域网中使用这一地址来传输网络包
发送方 MAC 地址48网络包发送方的 MAC 地址,接收方通过它来判断是谁发送了这个包
以太类型16使用的协议类型。下面是一些常见的类型,一般在 TCP/IP 通信中只是用 0800 和 0806这两种。0000-05DC:IEEE 802.3;0800:IP 协议;0806:ARP 协议;86DD:IPv6;

在生成 MAC 头部时,只需要设置上述 3 个字段就可以了。

首先看一下“以太类型”,这里我们直接填写表示 IP 协议的值 0800(十六进制)就可以了。

接下来是发送方 MAC 地址,这里填写网卡本身的 MAC 地址。MAC 地址是在网卡生产时写入 ROM 里的,只要将这个值独取出来写入 MAC 头部就可以了。

至于接收方 MAC 地址就有点复杂了。因为目前我们还不知道目标地址的 MAC 地址。我们通过路由表查询到的只是目标地址的 IP 地址。那么怎么样才能获取到接收方的 MAC 地址呢?答案是通过 IP 地址查询 MAC 地址。

通过 ARP 查询目标路由器的 MAC 地址

通过 IP 查询 MAC 地址需要使用 ARP(Address Resolution Protocol:地址解析协议)。其原理是:通过广播方法,将包发给连接同一以太网中的所有设备,询问谁的 IP 地址是目标 IP 地址,如果有设备响应,则返回相应 MAC 地址。

如果对方和自己处于同一个子网中,那么通过上面的操作就可以的到对方的 MAC 地址(如果路由器的设置正确,那么对方应该在同一子网,否则d对方无法作出 ARP 响应,这时只能认为对方不存在,包的发送操作就会失败)。然后,我们将这个 MAC 地址写入 MAC 头部,MAC 头部就完成了。

不过,如果每次发送包都要这样查询一次,网络中就会增加很多 ARP 包。  因此我们会将查询结果放到一块叫做 ARP 缓存 的内存空间中留着以后用。也就是说,在发送包时,先查询一下 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址,而当 ARP 缓存中不存在对方 MAC 地址是,则发送 ARP 查询。

我们可以通过 arp -a 命令查看 ARP 缓存表:

 arp -a
? (192.168.0.1) at d0:76:e7:67:2d:ba on en0 ifscope [ethernet]
? (192.168.0.106) at e8:9f:6d:a9:14:1c on en0 ifscope [ethernet]
? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
? (239.255.255.250) at 1:0:5e:7f:ff:fa on en0 ifscope permanent [ethernet]

有了 ARP 缓存,我们可以减少 ARP 包的数量,但如果总是使用 ARP 缓存中保存的地址也会产生问题。例如当 IP 地址发生变化时,ARP 缓存的内容就会和现实发生差异。为了防止这种问题的发生,ARP 缓存中的值在经过一段时间后会被删除,一般这个时间在几分钟左右。这个删除的操作非常简单粗暴,不管 ARP 缓存中的内容是否有效,只要经过几分钟就全部删掉,这样就不会出问题了。当地址从 ARP 缓存中删除后,只要重新执行一次 APR 查询就可以再次获得地址了。

这个策略能够在几分钟后消除缓存和显示的查询,但 IP 地址刚刚发生改变的时候,ARP 缓存中依然会保留老的地址,这是就会发生通信的异常。遇到这种情况,我们可以查看 ARP 缓存的内容,并手动删除过时的条目,删除的命令是 arp -d

拼接 MAC 头部

接下来我们将 MAC 头部加在 IP 头部的前面,整个包就完成了。到这里位置,整个打包的工作是由 IP 模块负责的。

有人认为,MAC 头部是以太网需要的内容,并不属于 IP 的职责范围,但从现实来看,让 IP 负责整个打包工作是有利的。

如果在交给网卡之前,IP 模块能够完成整个打包工作,那么网卡只要将打好的包发送出去就可以了。对于除 IP 以外的其他类型的包也是一样,如果在交给网卡之前完成打包,那么对于网卡来说,发送的操作和发送 IP 包是完全相同的。这样一来,同一块网卡就可以支持各种类型的包。至于接收操作,我们到后面会讲,但如果接收的包可以原封不动直接交给 IP 模块来处理,网卡就只要负责接收就可以了。这样一来,一块网卡也就能支持各种类型的包了。与其机械地设计模块和设备之间的分工,导致网卡只能支持 IP 包,不如将分工设计得现实一些,让网卡能够灵活支持各种类型的包。

总结

IP 模块根据路由表 Gateway 栏的内容判断应该把包发送给谁。

查询 MAC 地址需要使用 ARP。

参考文档

  • 《网络是怎样连接的》—— 户根勤

往期文章