上一章那个 curl 请求走到链路层时,内核做了一件事:用 ARP 喊了句"谁是 192.168.1.1",拿到 MAC,把 IP 包封进一个以太网帧发出去。这一章就把那一步拆开。核心问题只有一个——在同一个网段里,一台机器怎么找到另一台?
链路层(Link Layer):负责把数据在同一个物理网段内、从一块网卡送到另一块网卡。它认的地址不是 IP,而是 MAC。
1MAC 地址:网卡的身份证
MAC 地址(MAC Address)是网卡出厂自带的 48 位硬件地址,在链路层唯一标识"这一块网卡"。它写成 6 段十六进制,像 a4:83:e7:1b:2c:5f:前 3 字节是 OUI(厂商代号,Apple、Intel 各有自己的段),后 3 字节是厂商给每块网卡分配的序号。理论上全球唯一、烧在硬件里——虽然软件能改写它(MAC spoofing,正是你 702 课里那套把戏的基础)。
记牢下面这组对照,它是本章后面所有内容的地基:
- MAC 回答"你是谁"(哪块网卡)——是身份,跟着网卡走,搬到哪个网络都不变;
- IP 回答"你在哪"(在哪个网络的哪个位置)——是地址,换个网络就得换。
MAC 像身份证号,跟着人一辈子;IP 像你眼下的住址,搬家就变。一个标识身份、一个标识位置——这俩永远别混为一谈。
看一眼你自己网卡的 MAC,找输出里 link/ether(macOS 上是 ether)那一行:
ip link show # Linux
ifconfig # macOS(找 en0 下的 ether 行)
2以太网帧:链路层的信封
以太网帧(Ethernet Frame)是链路层传输的基本单位,帧头记着这帧"从哪块网卡来、到哪块网卡去"。结构大致如下:
单位:字节;宽度为示意,非真实比例
那个 2 字节的类型字段(EtherType)说明"信封里装的是什么":0x0800 是 IPv4,0x0806 是 ARP,0x86DD 是 IPv6。0.1 里那个 IP 包,就是作为数据(payload)装在这里、EtherType 标成 0x0800。
EtherType 就像一个 Rust enum 的判别式(discriminant)——接收方先读这个 tag,才知道该把 payload 当成哪种变体来解析(交给 IP 层?还是 ARP?)。没有它,网卡收到一串字节会完全不知道该交给谁。
payload 最大 1500 字节,这个上限叫 MTU(Maximum Transmission Unit)。记住这个数字,0.3 讲 IP 分片时我们会回来找它。
3交换机与广播:网段是什么
同一网段(Network Segment) = 一组能直接靠链路层帧互相送达、不经过路由器的机器。把它们连在一起的设备是交换机(Switch)。
交换机干的事很朴素:维护一张 MAC 地址表(记录"哪个 MAC 挂在哪个口"),收到帧就查表、从对应的口转发出去——它只看 MAC,不懂 IP。另外有一个特殊地址 ff:ff:ff:ff:ff:ff,叫广播地址(Broadcast):发往这个目的 MAC 的帧,本网段每台机器都会收到。下一节的 ARP 就靠它。
4ARP:从 IP 找到 MAC
回到 0.1 留下的难题:上层把目的 IP 交给你了,可链路层只认 MAC,怎么办?这正是 ARP(Address Resolution Protocol) 要解决的——通过广播把一个 IP 翻译成对应的 MAC:
- 主机广播一帧:"谁有 192.168.1.1?告诉 192.168.1.100"(目的 MAC 填
ff:ff:ff:ff:ff:ff,全网段都收到); - 只有 192.168.1.1 那台机器单播回一帧:"192.168.1.1 在 a4:83:e7:…";
- 主机拿到 MAC,缓存进 ARP 表(ARP cache),下次直接用,不必再问。
ARP 把"IP → MAC",DNS 把"域名 → IP"——都是一次地址解析,都带缓存来避免重复查询。你在 0.5 会正式见到 DNS;它俩是同一种思路在不同层的体现。
看看你机器上的 ARP 缓存,也就是它问过、记下来的邻居:
ip neigh # Linux:显示邻居及其状态(REACHABLE / STALE…)
arp -a # macOS / Linux 通用
ARP 是广播,而广播不跨路由器(交换机会转发广播帧,路由器不会)。所以 ARP 只在同一个网段内有效。那跨网段呢?当上层发现目的 IP 不在本网段,它根本不会去 ARP 那个远端 IP,而是 ARP 默认网关(你家路由器)的 MAC,把帧丢给网关、让它接力转发。"怎么判断在不在同一网段""网关如何接力",就是下一章 0.3 的全部内容。
你家路由器上那几个 LAN 口,背后其实是一个交换机——OpenWRT 把它们桥接成一个接口,通常叫 br-lan。桥(bridge)做的就是本节的交换机:把多个口并成一个广播域,口与口之间在链路层(二层)直接互通,ARP 和广播都在这个桥内部跑。而 LAN 到 WAN 那种跨网段转发,才轮到下一章的"路由"。读 OpenWRT 的 /etc/config/network 时,你会看到一个 device 类型为 bridge、带一组 ports 的配置——那就是这个 br-lan。
5实验:抓一次 ARP
理论讲完,抓一次 ARP 把整个过程看活。开两个终端:
# 终端 A:只抓 ARP(Linux 用 any;macOS 换成 -i en0)
sudo tcpdump -i any -n arp
# 终端 B:ping 同网段的一台机器
# (比如默认网关,先用 ip route 看它的地址)
ping -c 3 192.168.1.1
终端 A 里你会看到一问一答:
Request who-has 192.168.1.1 tell 192.168.1.100—— 广播发问Reply 192.168.1.1 is-at a4:83:e7:…—— 单播回答
ping 完再跑一次 ip neigh,会发现这个邻居已经被缓存进来了。你刚刚就是亲手走了一遍 0.1 里"内核用 ARP 找路由器 MAC"那一步。
看 ARP 缓存用 arp -a;抓包接口换成 -i en0;手动清掉某条缓存是 sudo arp -d <ip>(方便你重新触发一次 who-has)。看默认网关用 route -n get default。
本章小结
- 链路层只认 MAC,不认 IP。MAC 是网卡的身份(跟着网卡),IP 是位置(跟着网络)。
- 数据在链路层以以太网帧传输;帧头的 EtherType 说明 payload 是 IP / ARP / IPv6;payload 上限 1500 字节(MTU)。
- 交换机靠 MAC 地址表在二层转发;
ff:ff:ff:ff:ff:ff是广播。 - ARP 用广播把 IP 解析成 MAC,带缓存。关键:ARP 只在同网段有效,跨网段先发给网关——这是 0.3 的入口。
- 路由器的 LAN 口是一个桥(
br-lan),本质就是个交换机。
动手练习
- 跑
ip link show(或ifconfig),找出你网卡的 MAC,查一下前 3 字节的 OUI 属于哪个厂商(网上有 OUI 查询站)。 - 抓 ARP:
sudo tcpdump -n arp,ping 一台同网段机器,把那对 who-has / is-at 截下来。 - 思考题:你 ping 一个外网 IP(比如
8.8.8.8)时,tcpdump 抓到的 ARP 请求里,who-has 后面跟的是8.8.8.8,还是你的网关地址?为什么?(答案就藏在本章那条"关键结论"里。) - 进阶:用
ip neigh看缓存,找一条STALE或REACHABLE状态的记录,想想这个状态机跟 ARP cache 老化有什么关系。