Linux网络:数据链路层

之前我们学习了网络层的TCP/IP协议,本期我们就来学习网络链路层相关的内容

目录

数据链路与网络层

以太网

以太网帧结构

MAC地址

结构与表示

地址特性

[IP 地址的相对特性](#IP 地址的相对特性)

[MAC vs IP 的严谨对比](#MAC vs IP 的严谨对比)

协作模型:为什么两层地址缺一不可?

MTU

概念

[MTU 对 IP 协议的影响](#MTU 对 IP 协议的影响)

[IPv4 分片](#IPv4 分片)

[IPv6 彻底杜绝中间分片](#IPv6 彻底杜绝中间分片)

[MTU 对 UDP 协议的影响](#MTU 对 UDP 协议的影响)

分片的代价

最佳实践

[MTU 对 TCP 协议的影响](#MTU 对 TCP 协议的影响)

MSS------最大段大小

[路径 MTU 发现 (PMTUD)](#路径 MTU 发现 (PMTUD))

对性能的实质影响

总结与开发者的认知框架

ARP协议

[ARP 的本质与归属](#ARP 的本质与归属)

[ARP 工作流程(标准场景)](#ARP 工作流程(标准场景))

[ARP 缓存管理与生存周期](#ARP 缓存管理与生存周期)

ARP协议格式


数据链路与网络层

数据链路层 :解决同一网段内、相邻节点之间的可靠(或不可靠)帧传输。

网络层 :解决跨越多个网段、任意主机之间的数据包传输和路径选择。

它们之间的对比如下:

对比维度 数据链路层 (Layer 2) 网络层 (Layer 3)
寻址方式 硬件地址(MAC 地址,48 bit),扁平结构,无层次 逻辑地址(IPv4/IPv6 地址),有网络号与主机号的层次结构
协议数据单元 (PDU) 帧 (Frame) 包 (Packet)
转发依据 MAC 地址表(由自学习构建),按目标 MAC 查找出口 路由表(静态、动态路由协议),按目标 IP 做最长前缀匹配
典型互连设备 交换机(二层交换机)、网桥 路由器、三层交换机
作用范围 一个广播域内(即一个局域网或 VLAN) 跨广播域,可连接不同网络(互联网)
是否分段 无分段能力,MTU 一般由物理层限制(如以太网 1500 字节) 具备分片与重组能力(IPv4 路由器可分片,IPv6 由源节点分片)
可靠性与流量控制 某些协议(如 Wi-Fi MAC)提供确认与重传;以太网仅提供 CRC 校验,不保证可靠 IP 本身是尽力交付(无连接、无确认、无重传),可靠交付由上层(TCP)负责
广播/组播控制 广播帧泛滥在同一广播域,不可穿越路由器 路由器隔离广播域,三层广播(如 255.255.255.255)受限不可路由
核心协议例子 以太网 (IEEE 802.3)、Wi-Fi (802.11)、PPP IPv4、IPv6、ICMP、IGMP
关键控制协议 无(生成树 STP 算二层防环协议) ARP(介于二三层之间,用二层广播完成三层 IP→MAC 映射)

以太网

以太网是一套完整的链路层协议

以太网帧结构

MAC地址

Media Access Control address ,即介质访问控制地址,是一个 48 位 的硬件标识符,用于在同一广播域(局域网)内唯一标识一个网络接口控制器(NIC)。它是二层帧转发的唯一依据。

结构与表示

  • 长度:6 octets(48 bits)。

  • 表示法:十六进制冒号分隔,如 52:54:00:12:34:56

  • 结构:

    • 前 24 位:组织唯一标识符 (OUI),由 IEEE RA 分配给各厂商。

    • 后 24 位:设备标识,由厂商保证在同一个 OUI 内唯一。

  • 两个特定位:

    • I/G 位(第一个字节最低位):0 表示单播地址,1 表示组播/广播。

    • G/L 位(第一个字节次低位):0 表示全球唯一(出厂烧录),1 表示本地管理(软件修改)。

地址特性

  • 扁平性:无网络号/主机号层次,只是 48 bit 的一维编号。无法聚合,无法做层次化路由。

  • 全球唯一性(规范上):OUI 制度保证了烧录地址全球唯一,但可被驱动程序修改(导致冲突风险)。

  • 作用域仅在一个广播域(Layer 2 domain)内有效。跨越路由器后,源和目标 MAC 都会被替换。

IP 地址的相对特性

IP 地址(IPv4 32 bit,IPv6 128 bit)是逻辑地址,作用于网络层,提供全局可路由的终端标识。

  • 层次性:网络前缀 + 主机号,支持地址聚合,极大压缩路由表规模。

  • 分配灵活性:按拓扑分配,终端移动至不同网络会获得不同 IP。

  • 作用域在整个互联网中保持端到端一致性。IP 包头中的源、目的 IP 在转发过程中通常不改变(除非 NAT)。

MAC vs IP 的严谨对比

维度 MAC 地址 IP 地址
所属层 数据链路层(Layer 2) 网络层(Layer 3)
地址长度 48 bit IPv4 32 bit,IPv6 128 bit
结构 扁平,无层次 分层:网络前缀 + 主机号
分配机构 IEEE RA → 厂商 OUI IANA → 区域互联网注册机构 → ISP/企业
唯一性保证 出厂烧录全球唯一(可软件覆盖) 在同一个地址域内按规划分配唯一
地址分类 单播、组播、广播 单播、组播、广播(受限)、任播(IPv6)
作用范围 一段链路或一个广播域 可跨网络,全球可路由(公网地址)
在转发中的行为 每跳改变(重新封装帧) 目的 IP 全程不变,源 IP 也可能不变(除 NAT)
寻址/查表 交换机 CAM 表做精确匹配(Exact Match) 路由器 FIB 做最长前缀匹配(LPM)
解析方向 直接用,但需 ARP 将 IP 映射为 MAC DNS 将域名解析为 IP,DHCP 动态分配
编程接口 AF_PACKET, struct ethhdr AF_INET, struct iphdr

关键差异一句话

  • MAC 回答"在这个网段里,帧下一跳交给谁"。

  • IP 回答"这个数据包最终要到世界的哪个终端"。

协作模型:为什么两层地址缺一不可?

数据从主机 A(IP-A,MAC-A)发往不同子网的主机 B(IP-B,MAC-B)时:

  1. 网络层封装:A 的协议栈构建 IP 包,目的 IP = IP-B(全程不变)。

  2. 路由决策:查询路由表,下一跳为网关 R1 的 IP。

  3. 链路层封装:通过 ARP 获得 R1 接口的 MAC-R1,以此为目标 MAC 构建帧。

  4. 第一跳转发:帧到达 R1,R1 拆除帧头,取出 IP 包,再查路由,重复封装新帧(源 MAC=R1 出口,目标 MAC=R2 入口)。

  5. 最终跳:最后一跳路由器发现 B 在直连网段,用 ARP 查到 MAC-B,目的 MAC 改为 MAC-B。

这清晰地显示:IP 保持端到端语义,MAC 逐跳替换完成实际交付。没有 MAC 地址,数据无法跨越物理链路;没有 IP 地址,数据跨不出一个局域网。

MTU

概念

MTU 是数据链路层帧所能承载的最大载荷长度,即帧中除去链路层头部和尾部后,所能装载的最大上层协议数据量。

以标准以太网为例:

  • 以太网帧头(目的MAC + 源MAC + EtherType):14 字节

  • 帧尾 FCS (CRC):4 字节

  • 最大帧总长(不计前导码):1518 字节

  • MTU = 1518 - 14 - 4 = 1500 字节

另需注意一个相关概念:

  • 路径 MTU (Path MTU):从源到目的整条路径上所有链路 MTU 的最小值。

在 Linux 下,你可以通过 ioctl 获取接口 MTU:

cpp 复制代码
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
int get_mtu(const char *ifname) {
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    struct ifreq ifr;
    strcpy(ifr.ifr_name, ifname);
    ioctl(fd, SIOCGIFMTU, &ifr);
    int mtu = ifr.ifr_mtu;
    close(fd);
    return mtu;
}

MTU 对 IP 协议的影响

IP 层负责将上层数据(如 TCP 段或 UDP 数据报)封装进 IP 包。当包长超过出口链路的 MTU 时,IP 层必须处理"装不下"的问题。

IPv4 分片

IPv4 允许源端或中间路由器对数据包进行分片,目的端重组。

关键字段:

  • DF 位 (Don't Fragment):若置 1,禁止分片。路由器会丢弃包并回送 ICMP "需要分片但 DF 置位" (Type 3, Code 4) 消息,告知 MTU 值。

  • MF 位 (More Fragments):除最后一片外均置 1。

  • Offset 字段:指示片在原始包中的偏移(单位 8 字节)。

分片对终端性能的影响:

  • 增大了丢包概率:丢失一个分片,整个原始包即不可用,且需要全部重新传输。

  • 加重目的端重组负担(内存、CPU)。

  • 某些安全设备会丢弃分片(防御攻击)。

在 C++ 中,你可以通过原始套接字手动构造 IP 头来控制分片,但更常见的操作是设置 DF 标志:

cpp 复制代码
int val = IP_PMTUDISC_DO; // 总是设置 DF
setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));

当发送一个禁止分片但长度超过路径 MTU 的包时,send 会返回 EMSGSIZE 错误,应用程序可以据此调整数据大小。

IPv6 彻底杜绝中间分片

IPv6 强制源端进行分片,中间路由器绝不执行分片。遇超过出口 MTU 的包,直接丢弃并回送 ICMPv6 "Packet Too Big"。因此,IPv6 必须依赖路径 MTU 发现 (PMTUD),否则通信可能失败。

MTU 对 UDP 协议的影响

UDP 是面向数据报的协议,应用程序交给 UDP 的数据大小直接对应一个 IP 包的数据部分。因此,UDP 数据报极易遭到 IP 分片

分片的代价

假设你要发送 4096 字节的 UDP 数据报,以太网 MTU 1500 字节,扣除 IP 头(20 字节)和 UDP 头(8 字节),可用载荷为 1472 字节。该数据报会被切成 3 个分片:

  • 片 1:IP头 + 1472 字节(MF=1, Offset=0)

  • 片 2:IP头 + 1472 字节(MF=1, Offset=185)

  • 片 3:IP头 + 1152 字节(MF=0, Offset=369)

若任一片丢失,整个数据报丢弃。在有 1% 丢包率的链路上,一个 3 片报文的全成功概率仅 0.99³ ≈ 97%,显著低于小报文。

最佳实践
  • 应用层限制 :将 UDP 载荷大小控制在 MTU - 28 字节以内(20 IP 头 + 8 UDP 头),即通常的 1472 字节以内,以避免分片。DNS 协议就是典型,传统限制为 512 字节有效载荷,扩展机制 EDNS0 可协商更大缓冲,但明智的公共 DNS 实现仍会限制路径 MTU。

  • 设置 DF 位:UDP 发送时也可启用 DF,通过失败反馈确定路径 MTU,但多数简单的 UDP 应用不处理,仍依赖分片。

  • 数据报分段重传:若需大块传输且要求可靠,应在上层设计切分与确认机制(如 QUIC 协议那样),而不是让 IP 层分片。

从编程角度看,发送超长 UDP 报文的代码可能非常简单:

cpp 复制代码
int sock = socket(AF_INET, SOCK_DGRAM, 0);
const char *msg = huge_data; // 长度 2000
sendto(sock, msg, 2000, 0, (sockaddr*)&addr, sizeof(addr));
// 如果未设置 DF,内核会悄无声息地进行 IP 分片。

如果你想主动防止分片:

cpp 复制代码
int df = 1; // 或用 IP_PMTUDISC_DO
setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &df, sizeof(df));
if (sendto(...) < 0 && errno == EMSGSIZE) {
    // 需要减小数据报大小
}

MTU 对 TCP 协议的影响

TCP 是面向字节流的协议,有内建机制巧妙规避 IP 分片,尽量使每个 IP 包恰好填满链路层帧。

MSS------最大段大小

TCP 三次握手时,通过选项字段协商 MSS。MSS 就是 TCP 段中数据部分的最大长度,通常等于:

复制代码
MSS = MTU - IP首部长度(20) - TCP首部长度(20) = MTU - 40

标准以太网下 MSS = 1460 字节。这样 TCP 段封装成 IP 包后刚好不超过 MTU,从根本上杜绝分片。

内核会自动根据出口接口的 MTU 设置 MSS。你可以通过套接字选项获取本地 MSS(但不建议手动修改):

cpp 复制代码
int mss;
socklen_t len = sizeof(mss);
getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &mss, &len);
路径 MTU 发现 (PMTUD)

即使两端 MSS 按本地 MTU 计算,中间链路可能存在更小的 MTU(如隧道)。TCP 会通过 PMTUD 动态调整:

  • 发送方设置 DF 位,发送一个等于当前 MSS 的段。

  • 若中间路由器因 MTU 太小而丢弃,回送 ICMP "需分片"并告知该链路 MTU。

  • 发送方据此降低 MSS,重传以适配路径 MTU。

PMTUD 是 TCP 可靠传输的重要辅助,但常遭遇 ICMP 黑洞 :网络管理员屏蔽 ICMP 消息,导致发送方持续发大包而丢包。此时 TCP 尝试几次后若握手失败,可能退化为低效率。为解决此问题,产生了 PLPMTUD(分组化层路径 MTU 发现,RFC 4821),TCP 通过探测不同大小的包来主动搜索可用 MTU,不依赖 ICMP。

对性能的实质影响
  • 若 MTU 变小,每个 TCP 段的 MSS 变小,TCP 有效吞吐量降低(报头开销占比增加)。极端情况如某些 VPN 隧道 MTU 降至 1300,则 MSS = 1260,传输同样数据需要更多段。

  • TCP 流量控制与拥塞控制均基于段(segment)数量,较小 MSS 可能加剧某些场景的 CPU 负载,但避免分片换来的稳定性收益更为重要。

  • 在 C++ 高性能服务端开发中,当需精细调优网络参数时,了解本地接口 MTU 和路径 MTU 对 TCP buffer 大小的影响至关重要。例如,SO_RCVBUFSO_SNDBUF 的设置应考虑到 BDP(带宽延迟积),而 BDP 与有效 MSS 有关。

总结与开发者的认知框架

  • MTU 是链路层的物理上限,IP 层必须遵从。

  • 对 IP:分片是迫不得已的后备手段,IPv4 尚可用,IPv6 则强制退出中间路由器,逼迫源端重视 MTU 发现。

  • 对 UDP:分片危险且无补偿机制,开发者必须主动将数据报控制在一定大小内,或在上层自行实现可靠传输与分段。

  • 对 TCP:MSS 协商 + PMTUD 自动规避分片,TCP 栈已为我们做好绝大多数工作,但要注意防火墙对 ICMP 的干扰,以及跨隧道时 MTU 的异常。

从 C++ 网络编程接口看,你掌握着检测、控制、规避 MTU 的能力:

  • ioctl(SIOCGIFMTU) → 获取接口 MTU

  • setsockopt(IP_MTU_DISCOVER) → 控制分片策略和 DF 位

  • getsockopt(TCP_MAXSEG) → 查看协商的 MSS

  • 错误处理中识别 EMSGSIZE 和 ICMP 带来的 EINVAL 或超时征兆

ARP协议

ARP 的本质与归属

  • 本质:一个请求---应答协议,将 32 位 IPv4 地址动态解析为 48 位 MAC 地址。

  • 所在层次:传统上视为 2.5 层协议------直接封装在以太网帧中(EtherType = 0x0806),无 IP 头部,但其服务对象是网络层。

  • 作用范围 :严格限制在一个广播域(即一个局域网或 VLAN)内,ARP 请求是广播帧,永远不会被路由器转发

  • 对应 IPv6 的协议:NDP(邻居发现协议,使用 ICMPv6),不再用 ARP。

ARP 工作流程(标准场景)

假设主机 A(10.0.0.1)需要与同子网主机 B(10.0.0.2)通信,但不知道 B 的 MAC 地址。

  1. 检查 ARP 缓存

    • A 先查找本机 ARP 表(arp -n)。若已存在 10.0.0.2 的 MAC 条目,直接使用,跳过后续步骤。
  2. 发送 ARP 请求

    • 若无缓存,A 构造 ARP 请求报文:

      • 操作码 = 1

      • 发送方 IP / MAC = A 的

      • 目标 IP = 10.0.0.2

      • 目标 MAC = 00:00:00:00:00:00

    • 封装成以太网帧:目的 MAC = 广播 ff:ff:ff:ff:ff:ff,源 MAC = A 的 MAC,EtherType = 0x0806。

    • 该帧在整个广播域中泛洪,所有主机的网卡都会收到。

  3. 主机 B 响应

    • B 的网卡收到帧,发现 EtherType 为 ARP,交给 ARP 处理。

    • B 看到目标 IP 是自己的 10.0.0.2,于是记录 A 的 IP-MAC 映射到自己的 ARP 缓存(主动学习)。

    • 构造 ARP 应答:

      • 操作码 = 2

      • 发送方 IP / MAC = B 的

      • 目标 IP / MAC = A 的

    • 封装成以太网帧:目的 MAC = A 的 MAC(单播),直接发给 A。

  4. A 学习并缓存

    • A 收到应答,提取 B 的 MAC,存入 ARP 缓存,老化计时器(通常 20 分钟)开始倒计时。

    • 之后发往 B 的数据帧即可使用正确的目的 MAC。

ARP 缓存管理与生存周期

ARP 缓存是内核维护的一张核心表,在 Linux 中可通过 /proc/net/arpip neigh 查看,每个条目有以下状态:

  • REACHABLE:正常可用。

  • STALE:老化后,使用前需先确认(可达性检测)。

  • DELAY / PROBE:处于探测过程中的状态。

  • FAILED:多次探测无响应。

老化机制存在"被动更新"与"主动垃圾回收"。C++ 编程中可通过 Netlink 或 ioctl 操作此表。

ARP协议格式

注意到源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。

硬件类型指链路层网络类型,1为以太网;

协议类型指要转换的地址类型,0x0800为IP地址;

硬件地址长度对于以太网地址为6字节;

协议地址长度对于和IP地址为4字节;

op字段为1表示ARP请求,op字段为2表示ARP应答。

本期内容就到这里了,喜欢请点个赞谢谢

封面图自取:

相关推荐
派星6 小时前
SpringBoot全局异常处理
后端
不甘先生6 小时前
Go 四层架构实战:Handler + Service + Repository + Entity(清晰、可控、可演进)
开发语言·架构·golang
skilllite作者6 小时前
Warp 终端效能与交互体验全景展示
人工智能·后端·架构·rust
Yang-Never6 小时前
Git -> Git Worktree 工作树
android·开发语言·git·android studio
riNt PTIP6 小时前
GO 快速升级Go版本
开发语言·redis·golang
环流_6 小时前
IP协议特性
网络·tcp/ip·智能路由器
xingpanvip6 小时前
星盘接口开发文档:日运语料接口指南
android·开发语言·前端·css·php·lua
AI进化营-智能译站6 小时前
ROS2 C++开发系列01:在ROS2上编写第一个C++ hello word
开发语言·c++·ai·word
我才是一卓6 小时前
2026 Python 入门教程,结合 vscode 和 miniforge/miniconda
开发语言·vscode·python