二层与三层:ARP、VLAN、子网与转发
交换机怎么决定往哪个口发,路由器怎么决定下一跳,以及同网段与跨网段的区别。
学完这节你能做到
- 解释 ARP 的作用,并说出 ARP 表异常会造成什么现象
- 看懂 CIDR 前缀,快速算出可用地址数与广播域大小
- 读懂一台 Linux 主机的路由表并预测某个目的地址走哪条路
建议先学跳过这几节会看不懂本节的部分推导
只有两种情况
上一节说了 MAC 管同网段、IP 管跨网段。这一节把它落到具体动作上。
一台机器要发包,只需要判断一件事:目标在不在我这个网段?
目标在同一网段 → 直接发给它(二层转发,需要知道它的 MAC)
目标不在同一网段 → 发给网关,让网关去操心(三层转发)
就这两种情况。所有的路由表、ARP、VLAN 都是围绕这个判断展开的。
怎么判断"同不同网段"
靠子网掩码。看一个真实的地址配置:
ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet 10.0.12.34/24 brd 10.0.12.255 scope global eth0
/24 是掩码长度,含义是:IP 的前 24 位是「网段」,剩下 8 位是「这个网段里的第几台」。
10.0.12.34/24
└─────┬──┘└┬┘
网段 主机号
10.0.12.0 ~ 10.0.12.255 都属于同一网段
判断过程就是把双方的 IP 都按掩码"切一刀",比较网段部分:
| 目标地址 | 网段(按 /24 切) | 同网段? | 怎么发 |
|---|---|---|---|
10.0.12.99 | 10.0.12 | ✓ | 直接发 |
10.0.13.5 | 10.0.13 | ✗ | 交给网关 |
8.8.8.8 | 8.8.8 | ✗ | 交给网关 |
常见掩码要有数量感
| 掩码 | 可用主机数 | 常见用途 |
|---|---|---|
/30 | 2 | 两台设备互联的点对点链路 |
/24 | 254 | 最常见,一个机架或一个 VLAN |
/22 | 1022 | 一组机架 |
/16 | 65534 | 整个机房,或 K8s 的 Pod CIDR |
/8 | 一千六百多万 | 10.0.0.0/8 整个私有网段 |
/24 是 256 个地址。掩码每减 1,地址数翻倍:
/24 = 256 /23 = 512 /22 = 1024
/25 = 128 /26 = 64 /27 = 32
实际可用要减 2(一个网络号、一个广播地址)。所以 /24 是 254 台,不是 256 台。
这个数量感在 L3 规划那节要反复用:给 K8s 划 Pod CIDR、给机房划 VLAN, 算错一位就是十几倍的差距。
三个私有网段
这三段不会出现在公网上,机房和家里用的都是它们:
10.0.0.0/8 最大,云上和机房最常用
172.16.0.0/12 Docker 默认用 172.17.0.0/16 就在这一段里
192.168.0.0/16 家用路由器最常见
看到这三类地址,就知道它是内网地址,公网访问不到。
ARP:同网段还差一步
判断出"同网段、直接发"之后,还有个问题:网卡发包需要目标的 MAC 地址,而你只知道 IP。
ARP 就是干这个的——在网段里吼一声:
主机 A 广播: 谁是 10.0.12.99?请告诉我你的 MAC
主机 B 回答: 10.0.12.99 是我,我的 MAC 是 b8:ce:f6:2a:1c:41
结果会缓存起来,不用每次都问:
# 看 ARP 缓存(邻居表)
ip neigh
10.0.12.1 dev eth0 lladdr 0c:42:a1:5b:2e:80 REACHABLE ← 网关
10.0.12.99 dev eth0 lladdr b8:ce:f6:2a:1c:41 REACHABLE
10.0.12.77 dev eth0 FAILED ← 问了没人应
三个状态值得认识:
| 状态 | 含义 |
|---|---|
REACHABLE | 正常,最近确认过 |
STALE | 缓存还在但有点旧了,下次用会重新确认 |
FAILED | 问了没有回应 —— 这个 IP 在本网段里不存在,或者它挂了 |
1. FAILED 但对方确实活着
说明你们其实不在同一个网段(掩码配错了),或者中间的交换机端口/VLAN 配置有问题。 ARP 是广播,广播到不了对方就永远拿不到 MAC。
2. 同一个 IP 对应的 MAC 变来变去
大概率是IP 冲突——两台机器配了同一个地址,谁最后应答就用谁的。 症状是"时好时坏",非常难查。验证方法:
# 看这个 IP 到底有几个 MAC 在应答
arping -c 5 10.0.12.99顺便记一个词:gARP(无请求 ARP)——不等别人问,主动广播"这个 IP 现在归我了"。 K8s 里 MetalLB 的 L2 模式就靠它宣告 VIP,主备切换时也靠它让全网更新缓存。 L4 有一节专门讲它。
交换机:按 MAC 转发
交换机的工作简单到有点朴素,就三步:
1. 收到一个包,记下「源 MAC 来自哪个口」 ← 这叫 MAC 学习
2. 查目标 MAC 在自己的表里对应哪个口
3. 查到了 → 只往那个口发;查不到 → 往所有口发(洪泛)
所以交换机的表是自己学出来的,不用配置。这也是它和路由器最大的区别: 交换机只看 MAC、只管同网段;路由器看 IP、负责跨网段。
| 交换机 | 路由器 | |
|---|---|---|
| 看什么 | MAC 地址 | IP 地址 |
| 管什么 | 同网段内转发 | 网段之间转发 |
| 表怎么来 | 自动学习 | 配置或路由协议 |
| 广播 | 会转发(同一广播域) | 不转发 |
VLAN:把一台交换机切成几台
一台 48 口交换机,如果所有口都在同一个网段,那就是一个大广播域—— 任何一台机器的 ARP 广播全部 48 个口都收得到。机器多了,广播就成了负担; 而且任何两台机器天然互通,没有隔离。
VLAN 解决这个:给每个端口打一个编号,只有同一个编号的端口之间能二层互通。
交换机
├─ 端口 1-16 VLAN 10 → 生产网 10.0.10.0/24
├─ 端口 17-32 VLAN 20 → 测试网 10.0.20.0/24
└─ 端口 33-48 VLAN 30 → 管理网 10.0.30.0/24
VLAN 10 里的机器看不到 VLAN 20 的广播,要通只能走三层(经路由器)
两种端口类型:
| 类型 | 说明 | 用在哪 |
|---|---|---|
| access | 只属于一个 VLAN,包里不带标签 | 接服务器 |
| trunk | 承载多个 VLAN,包里带 802.1Q 标签标明归属 | 交换机之间、接虚拟化宿主机 |
服务器上要同时接多个 VLAN 时,可以在一张物理网卡上建子接口:
# 在 eth0 上建一个 VLAN 10 的子接口
ip link add link eth0 name eth0.10 type vlan id 10
ip addr add 10.0.10.34/24 dev eth0.10
ip link set eth0.10 up
802.1Q 标签插在以太网头里,占 4 字节。所以 trunk 链路上如果 MTU 还是 1500, 实际能承载的净荷会少 4 字节。
平时不影响,但在跑 overlay(L1 会讲,还要再减 50 字节)或者 RoCE 时, 这 4 字节要一起算进 MTU 账里。MTU 相关的故障几乎都来自"某处少算了几个字节"。
路由表:本机的决策依据
跨网段的包要交给网关,那"哪个是网关"写在哪?路由表。
ip route
default via 10.0.12.1 dev eth0 ← 兜底:其它全交给网关
10.0.12.0/24 dev eth0 proto kernel scope link ← 本网段:直连,不用网关
10.20.0.0/16 via 10.0.12.254 dev eth0 ← 特定网段走另一个下一跳
172.17.0.0/16 dev docker0 proto kernel scope link ← Docker 建的
读法就三种:
| 形态 | 含义 |
|---|---|
X.X.X.X/n dev eth0 scope link | 直连,同网段,直接发(ARP 拿 MAC) |
X.X.X.X/n via A dev eth0 | 去这个网段,交给下一跳 A |
default via A | 兜底,前面都没匹配上就交给 A |
匹配规则:前缀最长的优先。 不是从上往下,而是"哪条最具体用哪条":
目标 10.20.5.9
10.20.0.0/16 via 10.0.12.254 ← 匹配 16 位,更具体,用这条
default via 10.0.12.1 ← 只匹配 0 位,兜底
最有用的一条命令是让内核直接告诉你结果,不用自己推:
ip route get 10.20.5.9
10.20.5.9 via 10.0.12.254 dev eth0 src 10.0.12.34
↑ 下一跳 ↑ 出口网卡 ↑ 源地址
"这个包会从哪张网卡出去、交给谁"——这个问题不要靠看表推理,直接问内核。
多网卡机器(存储节点、GPU 节点、有 VPN 的机器)上路由表往往几十条,
外加策略路由(ip rule)参与决策,人工推理很容易错。ip route get 给的是内核真实的决策结果。
排查"为什么走错网卡了",第一条命令就是它。
把一次访问连起来
回到上一节那条 curl,现在能把二三层这部分补完整了:
curl https://example.com
↓
① DNS 拿到 IP:93.184.216.34
↓
② 本机判断:93.184.216.34 不在 10.0.12.0/24 里 → 跨网段
↓
③ 查路由表:没有更具体的匹配 → default via 10.0.12.1
↓
④ 网关 10.0.12.1 在同网段 → 查 ARP 拿它的 MAC
↓
⑤ 包发出去:目的 IP 是 93.184.216.34,目的 MAC 是网关的 MAC
↓
⑥ 网关继续往下一跳转发,每跳都换一次 MAC,IP 始终不变
注意第 ⑤ 和 ⑥ 步这个关键区别:
- IP 地址全程不变(它标识最终目标)
- MAC 地址每一跳都换(它只标识"这一段路的下一个设备")
这一条理解了,二三层的分工就真的通了。
检查点
本机地址是 10.0.12.34/24,要访问 10.0.13.5。会怎么发?
ip neigh 显示某个同网段 IP 是 FAILED,但你确认那台机器是开着的。最该怀疑什么?
路由表里同时有 10.20.0.0/16 via A 和 default via B。访问 10.20.5.9 走哪条?
这节课的落点
- 发包只判断一件事:目标在不在本网段。在 → 直接发;不在 → 交给网关
- 掩码决定网段边界;
/24= 254 台可用,掩码每减 1 地址数翻倍 - 三个私有网段:
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 - ARP 用广播把 IP 换成 MAC;
ip neigh看缓存,FAILED说明广播到不了对方 - 同一 IP 的 MAC 变来变去 = IP 冲突,用
arping验证 - 交换机看 MAC、表自动学习、转发广播;路由器看 IP、不转发广播
- VLAN 把一台交换机切成多个广播域;access 接服务器、trunk 带标签;标签占 4 字节
- 路由表三种形态:直连
scope link、指定下一跳via、兜底default;最长前缀优先 ip route get <目标>是排"走错网卡"的第一条命令- 全程 IP 不变、MAC 每跳都换
延伸资料
- ·Systems Performance, 2nd Edition — Brendan Gregg