以太网、ARP 与交换机
环境:Windows 11 x64,GCC 13.1.0(MinGW-w64),自写的 mini-tcpdump 直连 Npcap(
datalink=1的真以太网口,pcap_open_live的 promisc 参数给 1)。抓包对象是本机”以太网”(Realtek PCIe GbE,MTU 9000,1 Gbps)所在的那段局域网。所有帧字段都是从字节流里解出来的;ARP 欺骗、广播风暴这类要污染邻居表或打扰别人的实验,本节没做,理由写在最后。
一、MAC 帧与 MTU:本机三个数字
Get-NetAdapter 的实物清单(节选):
ifIndex Name MacAddress LinkSpeed Status
20 以太网 9C-2D-CD-A8-AB-7D 1 Gbps Up
24 VMware Network Adapter VMnet1 00-50-56-C0-00-01 100 Mbps Up
26 VMware Network Adapter VMnet8 00-50-56-C0-00-08 100 Mbps Up
22 cfw-tap 00-FF-BF-79-14-14 1 Gbps Disconnected
8 cherry (无 MAC) 100 Gbps Up
两个值得记住的结论:
- 虚拟网卡自己不发二层。
cherry那条隧道口没有 MAC、报 100 Gbps,因为它根本没有链路层,帧由 ifIndex 20 那张真网卡承担。看ipconfig /all找不到某张卡的物理地址时,别当成驱动坏了。 - MAC 前 3 字节是厂商,但能不能信它取决于设备。VMware 的
00-50-56-C0-00-xx一眼就是 VMnet1/VMnet8(OUI 属于 VMware)。本机 ARP 表里那批10-20-30-40-xx-xx就更直白——10-20-30-40-50-29、10-20-30-40-60-01是明显手工填的地址,见第三节。
MTU 用 DF 标志卡边界(ping -f -l <载荷> 192.168.103.254,目标就是本机网关,一共发了 4 个包):
--- size=1472 来自 192.168.103.254 的回复: 字节=1472 时间=1ms TTL=255
--- size=8972 (通过,8972 + 20 IP + 8 ICMP = 9000,正好占满)
--- size=8973 需要拆分数据包但是设置 DF。
“需要拆分数据包但是设置 DF”这条错误是本机协议栈生成的,一个包都没上网——它是查路由表和接口 MTU 之后直接拒绝。用这个办法可以逐接口把 MTU 量出来,比看配置可靠。回环侧的对应上限是 UDP 载荷 65507(65535−28),超一个字节就返回 10040 WSAEMSGSIZE。
帧长实测分布(同一批抓包):ARP 请求 frame=42(14 头 + 28 ARP),别的机器发来的 ARP frame=60。差的 18 字节是以太网最小帧填充——42 字节不够 64(含 FCS),网卡发出去前会补零。Npcap 抓到的是本机网卡补帧之前的长度,所以自己发的短帧看起来”不合规”。看到 42 字节的以太网帧别急着怀疑抓包工具。
二、ARP 缓存:把状态机看成一个一个的真实状态
网关已解析时:
IPAddress : 192.168.103.254
InterfaceAlias : 以太网
LinkLayerAddress : 58-8D-09-60-97-BF
State : Reachable
对一个不存在的地址(.99)发起一次访问,邻居表里立刻长出这条:
IPAddress : 192.168.103.99
LinkLayerAddress : 00-00-00-00-00-00
State : Unreachable
全零 MAC 加上 Unreachable,就是”ARP 没解析成功”的标准形状——不是路由问题,是二层就没找到人。抓包同时记录了失败过程:
[ 1043318] ARP who-has ethdst=ff:ff:ff:ff:ff:ff sender 192.168.103.24 [9c:2d:cd:a8:ab:7d] target 192.168.103.77 [00:00:00:00:00:00] frame=42 wire=42
[ 1043335] ARP who-has ethdst=ff:ff:ff:ff:ff:ff sender 192.168.103.24 [9c:2d:cd:a8:ab:7d] target 192.168.103.77 [00:00:00:00:00:00] frame=42 wire=42
两条一模一样的广播请求,间隔 17 微秒(Windows 的 ARP 探测是背靠背发的,不是每秒一条),之后没有任何 is-reply。同一个抓包窗口里也能看到”解析成功”的那一半:
[ 24886] ARP who-has ethdst=ff:ff:ff:ff:ff:ff sender 192.168.103.18 [10:ff:e0:59:48:7e] target 192.168.103.189 [00:00:00:00:00:00] frame=60 wire=60
[ 1596975] ARP is-reply ... (同一窗口里抓到 2 条应答、6 条请求)
缓存的规模与上限:arp -a -N 192.168.103.24 在这张卡上有 69 条邻居;netsh int ipv4 show global 给的上限是”邻居缓存限制:每接口 256 项”。也就是说这张 /24 才用了容量的 27%,但一个 /16 段里乱扫是能把缓存顶到上限并开始淘汰的。
还有两条静态项值得注意:
192.168.103.255 ff-ff-ff-ff-ff-ff 静态
255.255.255.255 ff-ff-ff-ff-ff-ff 静态
Windows 自己给子网广播地址留了一条静态 ARP(不会老化)。排查广播相关问题时,这两条是正常项,不是被人种了静态表。
三、ARP 欺骗能怎么”查”,而不是怎么”演”
构造 ARP 欺骗要把假映射推进本机的邻居表、还会影响同段其他机器——这属于本专栏统一划的红线之外的事,所以本节没有做攻击实验,只给在本机能落地的检查方法:
| 检查项 | 本机实测值 | 异常判据 |
|---|---|---|
| 网关 MAC | 58-8D-09-60-97-BF(Get-NetNeighbor,State=Reachable) | 同一个 IP 的 MAC 变了;或同时出现两个候选 |
| OUI 是否合理 | 网关 MAC 前 3 字节 58-8D-09 | 10-20-30-40-xx-xx 这类明显手工序列 |
| 是否广播流量异常放大 | 12 秒抓包:ARP 8 条、255.255.255.255 4 条 | 每秒成千条 who-has、ARP 应答比请求多 |
| 静态条目来源 | 只有两条广播地址是静态 | 出现别人种下的网关静态项 |
arp -a 条目数 | 69(上限 256/接口) | 短时间内条目数暴涨 |
上表第二行不是假设:本机 ARP 表里确实有 4 条 10-20-30-40-* 形态的动态条目(.8 / .106 / .150 / .250)。这可能是实验室设备的静态配置,也可能是伪造源,光看 ARP 表区分不了,必须去交换机侧看 MAC 表端口、或者对比这台主机其他时间的 MAC。这正是”ARP 没有认证机制”这件事在运维层的代价。
四、交换机学习与广播域:这台机器看到了什么
标准说法是”交换机按源 MAC 学习端口,单播只发给目标端口,广播/未知单播才泛洪”。本机的实测情况有点不一样,而且这个差异本身就是有价值的信息:
12 秒、不加过滤条件、混杂模式抓到的帧分布:
总帧数 4714,其中 ARP 8 条、TCP/UDP/ICMP 4665 条
源 IP 计数:
2726 192.168.103.24 ← 本机
1537 120.24.46.217 ← 公网对端
223 120.76.131.237 ← 公网对端
38 192.168.103.200 ← 别人的机器
17 192.168.103.18 ← 别人的机器
192.168.103.200 -> 1.12.12.12、192.168.103.18 -> ... 这类源和目的都不是本机的单播帧,本机网卡收到了。理想交换环境下这不该发生。三种解释:上联设备是共享介质(集线器/无线/AP 桥接)、端口做了镜像、或者对方流量目的地是本机所在广播域的组播/广播。结合 192.168.103.24 那张卡实际是”板载千兆但经由无线网桥”的常见形态,第一种可能性最大。结论:不要假设”我抓不到别人的单播”,先自己验一遍再下判断。
同一批帧里还看到广播域的真实尺寸:
目的为组播/广播的帧计数:
24 -> 224.0.0.22 (VRRP)
13 -> 239.255.255.250 (SSDP/WS-Discovery)
11 -> 224.0.0.251 (mDNS)
8 -> 224.0.0.252 (LLMNR)
4 -> 255.255.255.255 (受限广播)
其中 SSDP 那些包的 IP 头是 ttl=1(本机 netsh int ipv4 show global 里”多播转发 : disabled”,MLD 版本 : version3)。TTL=1 就是组播的”只走一跳”围栏:路由器不转发,所以它出不了这个广播域,但域内每台主机都得收一遍——这就是”组播比广播省,但省在路由器和全网范围,不省在你这一段”。
另一个反直觉观察:这段线路上同时跑着 192.168.103.x 和 192.168.3.x 两个网段的主机(抓到的 ARP 里有 192.168.3.1 [ba:54:9d:84:8f:7c] who-has 192.168.3.163)。二层广播域和三层子网不是一一对应的——交换机只认 MAC,把两个子网接进同一个 VLAN 完全可能,而且这时候两边的机器彼此能收到 who-has,只是 IP 层不会互访。路由表里 192.168.103.255 On-link、224.0.0.0/4 On-link 这些条目也只按接口划分,不区分子网。
五、VLAN 与 trunk:本节明确未实测
本机无法实测 VLAN/trunk,原因:手上没有可配的交换机来做打标签/剥离对照,Npcap 在 access 端口上也看不到”标签被剥掉之后”的样子。能给的只有一条否定证据:12 秒、4714 帧里 0 个 EtherType=0x8100(802.1Q)帧,出现的是 7 个 0x86dd(IPv6)和若干个 EtherType 值小于 0x0600 的 802.3 长度字段帧。也就是说本机这张卡接的是 access 端口、untagged 链路。
概念上按标准可以记这几点(都是协议规定,非本机实测):
- trunk 就是一条物理链路承载多个 VLAN,靠打 802.1Q 标签(在源 MAC 后插 4 字节:0x8100 + 3 位 priority + 1 位 CFI + 12 位 VID,VID 0–4094)区分。
- 打了标签帧就多了 4 字节,最大帧长从 1518 变 1522;如果同时开 jumbo,MTU 与 MSS 都要相应调整——本机 9000 MTU 对应抓到的 MSS=8960,就是”MTU−40”;换成 trunk 承载时实际可用载荷还会再少 4 字节。
- PVID/ native VLAN 决定未打标帧归属哪个 VLAN;VLAN 之间互通必须走三层(交换机三层模块或路由器),这也是”广播域被 VLAN 切小”的意义所在:上面第四节那种”一个口能听见 69 个邻居 + 别人家单播”的场面,划 VLAN 后会显著缩小。
六、小结
- MTU 是可以当场量的:
ping -f -l 8972过、8973报”需要拆分数据包但是设置 DF”(9000 MTU);回环上 UDP 载荷上限 65507,超了就是10040。MSS = MTU − 40 在本机 9000 链路上验证为 8960。 - ARP 的失败形状很实用:
LinkLayerAddress=00-00-00-00-00-00+State=Unreachable;who-has 会背靠背发两条(实测间隔 17µs);缓存上限本机是每接口 256 项,当前 /24 用了 69 项。 192.168.103.255和255.255.255.255在 ARP 表里是系统自带的静态条目,不是人为篡改。- 交换机学习只能验证一半:本机能收到别人的单播帧(38+17 帧来自 .200/.18),说明这段链路上”单播只发给目标端口”并不成立。默认别人家流量可见,是安全审计该有的起点,而不是相反。
- 广播域 ≠ 子网:同一线路上同时出现 192.168.103.x 与 192.168.3.x;VRRP/mDNS/LLMNR/SSDP 这些组播 + 4 个受限广播帧是”这段链路每秒都在交广播税”的直接证据,TTL=1 把它们关在域内。
- 未实测项:ARP 欺骗(要污染邻居表并影响同段其他机器)、VLAN/trunk 打标签(没有可配交换机,只给出”本端口无 0x8100 帧”的否定证据)。