1. 信息怎样变成网络中的字节
1.1 Bit、Byte 和十六进制
Bit(Binary Digit,二进制位)是计算机中最小的信息单位,只能取 0 或 1。
Byte(字节)通常由 8 个 bit 组成。一个字节可以表示 0..255。
网络协议经常使用Hexadecimal(十六进制),因为一个十六进制数字正好表示 4 个bit:
二进制 0000 = 十六进制 0
二进制 1001 = 十六进制 9
二进制 1010 = 十六进制 a
二进制 1111 = 十六进制 f
因此一个字节 11111111 可以写成 ff。IPv6 地址使用十六进制,MAC 地址也通常使用十六进制。
1.2 Network Byte Order
Network Byte Order(网络字节序)规定多字节整数在线上按高位字节在前的顺序 传输,也叫Big Endian(大端序)。
例如十六进制整数 0x12345678 在线上的四个字节依次是:
12 34 56 78
任何网络协议中的多字节整数都必须明确字节序。接收端不能直接假设主机内存顺序与网络顺序相同;C 代码通常通过 htonl、ntohl、htobe64 等函数转换。项目业务头中的具体字段等学到第七编再逐项解码。
1.3 Header 和 Payload
Header(头部)是协议为了传输和解释数据而添加的元信息;Payload(载荷) 是这一层实际承载的内容。
一个简化的嵌套关系是:
以太网Header
└─ IPv6 Header
└─ UDP Header
└─ Application Header(应用头)
└─ Application Data(应用数据)
对外层协议而言,整个内层通常只是 payload。例如,以太网不需要理解应用数据的字段。
1.4 Encapsulation 与 Decapsulation
Encapsulation(封装)是在原数据外面增加本层头部; Decapsulation(解封装)是接收端删除已经完成使命的外层头部。

图 1 封装:UDP 数据报装进 IP 包,IP 包再装进当前一跳的以太网帧;接收端按相反顺序剥离
例如,UDP 把应用数据封装成 UDP 数据报,IP 再把 UDP 数据报封装成 IP 包,Ethernet 最后把IP 包封装成当前一跳的帧。接收端按相反顺序剥离头部。项目中的多层 SRv6 封装等学完 IPv6后再解释。
1.5 校验题
- 一个字节有多少 bit,能表示多少个不同值?
- 对 Ethernet 层而言,整个 IPv6 包属于 header 还是 payload?
- 封装与修改内层数据有什么区别?
2. 为什么网络需要分层
2.1 常用四层视图
网络不是一个函数完成所有工作,而是分层协作。本书使用下面的简化视图:
| 层 | 常用英文 | 本层对象 | 主要问题 | 本项目例子 |
|---|---|---|---|---|
| 链路层 | Link Layer,链路层 | Frame | 同一条链路上交给哪个接口 | Ethernet、MAC、veth、XDP |
| 网络层 | Network Layer,网络层 | Packet | 跨多跳到哪个 IP 地址 | IPv4、IPv6、SRv6 |
| 传输层 | Transport Layer,传输层 | Datagram/Segment | 交给目的主机的哪个进程 | UDP、TCP |
| 应用层 | Application Layer,应用层 | Message/Object | 业务数据怎样解释 | 文件、图片、请求或响应 |
Frame(帧)是链路层传输单位;Packet(包)是 IP 层传输单位; Datagram(数据报)常指 UDP 单位;Segment(报文段)常指 TCP 单位。 日常日志有时会宽泛地把它们都称为“包”,读代码时必须看具体层次。
2.2 每层只解决自己的问题
以一段普通 UDP 应用数据为例:
- 应用层说明数据对业务意味着什么;
- UDP 用端口把它交给目的进程;
- IP 决定它跨越多跳后到哪个地址;
- Ethernet 用下一跳 MAC 完成当前一跳;
- 真实链路决定这一跳是否连通、能以多快速度传输。
如果链路层开始解释图片含义,或者控制服务替 Linux 逐包改 MAC,分层边界就被破坏了。
2.3 Control Plane、Data Plane 和 Observer Plane
Control Plane(控制面)交换邻居、拓扑、资源和策略所需的事实。 Data Plane(数据面)实际转发业务包并执行算子。 Observer Plane(观察面)记录事件、统计和图片,供分析与前端展示。

图 2 四层视图与三个平面:观察面故障不能停止数据面,控制器不代替节点做本地转发决策
本项目的底线是:观察面故障不能停止数据面;控制面不能承载被验证的业务数据;控制器不能代替节点完成本地转发决策。
3. Ethernet、MAC 和一跳交付
3.1 Ethernet 是什么
Ethernet(以太网)是一组常用链路层技术。本项目不模拟真实网卡电信号,但使用 标准 Ethernet 帧表达容器接口之间的一跳数据。
Ethernet 帧至少包含:
Destination MAC | Source MAC | EtherType | Payload
MAC(Media Access Control,介质访问控制地址)是链路层接口标识,通常写成
02:00:00:02:00:01。EtherType 说明 payload 是 IPv4、IPv6 还是其他协议。
3.2 MAC 只负责当前一跳
假设 IPv6 包最终要从节点 1 到节点 10,但节点 1 的当前下一跳是节点 2:
IPv6目的地址 = 节点10或某个SID
Ethernet目的MAC = 节点2在当前链路上的MAC
到节点 2 后,Linux 再次查询 FIB,为下一跳重建新的 Ethernet 头。IP 目的地址可以在多跳期间保持不变,MAC 目的地址通常每一跳都变化。
3.3 MTU
MTU(Maximum Transmission Unit,最大传输单元)是一条链路无需分片即可承载的 最大网络层包长度。IPv6 链路最小 MTU 是 1280 字节。本项目 AF_XDP frame 容量是 2048 字节,所有后续协议头与应用数据的总长度都必须保证完整帧不越界。
头部不是免费的。增加 SRH segment 会增加包长,所以 uSID 压缩能减少重复的 128-bit SID。
3.4 本项目的宿主机转发边界
节点 Linux 依据自己的 IPv6 FIB 和邻居表写入目标 MAC。宿主机 XDP/eBPF 只根据目标 MAC把帧 redirect 到对应 AF_XDP socket;AF_XDP 再把它放入 source→destination 有向链路。
这一节只需记住:节点 Linux 决定下一跳,宿主链路层实施这一跳的传输条件。宿主为什么默认不解释上层数据,以及项目唯一受限例外怎样工作,分别留到第三编和第八编。
4. IPv4 从零开始
4.1 IPv4 地址
IPv4(Internet Protocol version 4,互联网协议第 4 版)使用 32-bit 地址,通常按 四个十进制字节书写:
192.168.0.2
每段范围为 0..255。IPv4 地址标识一个网络接口在某个 IP 网络中的位置,不等同于用户、进程、Docker 容器或物理机器本身。
4.2 Prefix 与 Subnet
Prefix(前缀)表示一组共享高位 bit 的地址。192.168.0.0/24 中的 /24 表示前 24 个
bit 是网络部分,剩余 8 个 bit 是该网络中的接口部分。
Subnet(子网)是按前缀划分出的地址集合。例如:
网络:192.168.0.0/24
可写地址示例:192.168.0.2、192.168.0.5
本书示例只解释前缀匹配,不展开传统广播地址分配规则。
4.3 Default Gateway
Default Gateway(默认网关)是主机在没有更具体路由时交付流量的下一跳。
端侧不需要知道 200 颗卫星的全网拓扑。它只需要:
自己的地址
当前接入卫星的地址和MAC
一条指向接入卫星的默认路由
接入卫星才负责进入核心网络。
4.4 ARP
ARP(Address Resolution Protocol,地址解析协议)把同一 IPv4 链路上的下一跳 IP
解析为 MAC。例如,主机知道下一跳是 10.42.9.1,但发 Ethernet 帧前还要知道对应 MAC。
IPv6 不使用 ARP,而使用后面介绍的 NDP。
4.5 IPv4 头部中的关键字段
初学者需要先认识:
| 字段 | 作用 |
|---|---|
| Source Address | 源 IPv4 地址 |
| Destination Address | 目的 IPv4 地址 |
| TTL | 每经过一个三层转发节点减 1,防止路由环路无限存活 |
| Protocol | 指示上层是 ICMP、TCP、UDP 等 |
| Total Length | 整个 IPv4 包长度 |
TTL(Time To Live,生存时间)名字像时间,IPv4 转发时实际主要作为跳数上限。
4.6 本项目怎样使用 IPv4
外部飞机和船舶端侧可以发送普通 IPv4 业务。入口卫星执行 IPv4-in-IPv6 SRv6 封装,出口卫星使用 End.DX4 解封装并交给目的端侧。核心网络不需要给每个中间节点建立IPv4 SRv6 能力;核心转发外层 IPv6。
5. IPv6 从零开始
5.1 为什么地址变成 128 bit
IPv6(Internet Protocol version 6,互联网协议第 6 版)使用 128-bit 地址。它不仅 扩大地址空间,也提供扩展头机制,SRH 正是 IPv6 Routing Header 的一种。
IPv6 地址由 8 组 16-bit 十六进制数组成:
fd42:0001:0000:0000:0000:0000:0000:000a
每组可以省略前导零:
fd42:1:0:0:0:0:0:a
一段连续的全零组可以用一次 :: 压缩:
fd42:1::a
一个地址中不能出现两个 ::,否则无法判断各自代表多少组零。
5.2 IPv6 前缀
IPv6 也使用 CIDR 前缀。CIDR(Classless Inter-Domain Routing,无类别域间路由)用 地址/前缀长度 表达网络。示例:
fd42:1::/48
表示前 48 bit 固定。fd42:1::1 与 fd42:1::c8 都属于这个前缀。
常见长度:
/128:一个精确 IPv6 地址;/64:常见接口子网;/48或/32:可以继续划分的大定位前缀。
5.3 ULA
ULA(Unique Local Address,唯一本地地址)使用 fc00::/7 范围,作用类似组织内部
使用的 IPv6 地址,不直接作为全球互联网路由地址。本项目的 fd42:* 全部位于 ULA 范围,便于在封闭仿真中定义稳定地址合同。
5.4 Link-local Address
Link-local Address(链路本地地址)位于 fe80::/10,只在当前链路有效,通常不被
路由器跨链路转发。本项目节点间下一跳常写成:
via fe80::2 dev eth0
因为同一个 fe80::2 可能在不同接口上重复,Linux 命令必须同时给出 dev eth0。
5.5 Multicast
Multicast(组播)让一个发送者把报文交给一组监听者。IPv6 组播地址以 ff 开头。
本项目 HELLO 使用组播传播本地邻居探测,但只有实际收到并正确 ACK 的节点才能成为邻居。
组播不是广播。IPv6 没有 IPv4 那种全网广播地址。
5.6 NDP
NDP(Neighbor Discovery Protocol,邻居发现协议)通过 ICMPv6 完成 IPv6 地址到 MAC 解析、邻居可达性等功能。**ICMPv6(Internet Control Message Protocol for IPv6,IPv6 互联网控制消息协议)**承载错误报告和邻居发现等控制消息。
需要区分两个“邻居发现”:
- Linux NDP 解决“已知下一跳 IPv6 对应哪个 MAC”;
- 本项目 HELLO/ACK 解决“物理场景中哪个节点现在真的可达”。
它们工作层次不同,不能互相替代。
5.7 IPv6 基本头部
IPv6 基本头固定 40 字节。关键字段包括:
| 字段 | 作用 |
|---|---|
| Version | 固定为 6 |
| Payload Length | 基本头之后的字节数 |
| Next Header | 后面是扩展头、UDP、TCP 还是其他协议 |
| Hop Limit | 类似 IPv4 TTL,每次三层转发减 1 |
| Source Address | 128-bit 源地址 |
| Destination Address | 当前 128-bit 目的地址 |
5.8 Extension Header
Extension Header(扩展头)让 IPv6 在基本头之后串联额外功能。每个头用Next Header指向下一个头。SRH 是 Routing Extension Header(路由扩展头)的 Type 4。
一个 SRv6 封装包可以是:
Outer IPv6 Header
└─ SRH
└─ Inner IPv6 Header
└─ UDP
└─ Application Data
中间普通转发节点只需要按当前 IPv6 Destination Address 查询 FIB;只有当前目的地址命中本地 SID 的节点才执行对应 SRv6 行为。
5.9 IPv4 与 IPv6 不是“新旧开关”
| 对比 | IPv4 | IPv6 |
|---|---|---|
| 地址长度 | 32 bit | 128 bit |
| 常用写法 | 点分十进制 | 冒号分隔十六进制 |
| 邻居解析 | ARP | NDP/ICMPv6 |
| 跳数限制 | TTL | Hop Limit |
| 扩展机制 | 基本头选项能力有限 | Extension Header 链 |
| 本项目核心 SRv6 | 可作为内层业务 | 作为外层核心和内层业务 |
SRv6 依赖 IPv6 外层,但它可以承载 IPv4 或 IPv6 内层业务。
5.10 练习
- 展开
fd42:1::a为 8 组十六进制数。 /128和/48哪个匹配范围更精确?fe80::2为什么通常必须与接口名一起使用?- Linux NDP 与本项目 HELLO 分别发现什么?
6. 路由:包怎样逐跳前进
6.1 Route、Next Hop 和 Interface
Route(路由)是一条“某类目的地址应该怎样处理”的规则。典型路由包含:
destination prefix → next hop → output interface
Next Hop(下一跳)是当前节点下一次交付的相邻节点;Interface(接口)是包离开 本节点的出口。
例如:
fd42:1::a/128 via fe80::2 dev eth0
含义不是“节点 2 是最终目的”,而是“要去 fd42:1::a,当前先交给 fe80::2”。
6.2 Longest Prefix Match
Longest Prefix Match(最长前缀匹配)规定:若多条路由都匹配目的地址,选择前缀 长度最长,也就是最具体的一条。
fd42:1::/48 via A
fd42:1::a/128 via B
访问 fd42:1::a 选择 /128 的 B,访问 fd42:1::b 选择 /48 的 A。
Metric(度量值)可以在同等前缀的多条路由之间表达优先级,但它不能战胜更长前缀。
6.3 RIB 与 FIB
RIB(Routing Information Base,路由信息库)保存控制层面学到或配置的路由信息; FIB(Forwarding Information Base,转发信息库)是内核逐包转发实际使用的结果。
通俗类比:RIB 像规划部门的候选路线档案,FIB 像司机手上的当前导航指令。
本项目控制器不替节点安装 FIB。每个节点从收到的拓扑事实计算自己的主/备下一跳,再由唯一 forwarding worker(转发工作线程)调用 Linux 路由接口更新本地 FIB。
6.4 Neighbor Table
路由查询得到下一跳 IPv6 和接口后,还要通过Neighbor Table(邻居表)找到 MAC。所以转发需要两次不同意义的解析:
FIB:目的IPv6 → 下一跳IPv6 + 接口
邻居表:下一跳IPv6 + 接口 → 下一跳MAC
宿主机 XDP 最终读取的是已经写入 Ethernet 头的目标 MAC,不是自己重新查询全网路由。
6.5 Policy Routing
Policy Routing(策略路由)不仅看目的地址,还可以看报文 mark(标记)、源地址等 条件,选择另一张路由表。
普通路由:
destination → main table → route
一个通用策略路由例子:
packet mark = 7
→ ip rule选择table 10007
→ table 10007中的路由决定下一跳
这里先理解“同一目的可以因本地标记而查询不同路由表”。项目怎样从任务身份得到标记,以及怎样把它导入 SR Policy,到第四编再解释。
6.6 BFS 与最短跳数
BFS(Breadth-First Search,广度优先搜索)从起点按“一跳、两跳、三跳”逐层扩展。 在所有边代价相同的图中,它能找到最少跳数路径。
当前系统节点本地 FIB 使用单位边 BFS。物理场景虽然提供 delay_us 和 rate_bps,当前控制面选路成本仍是 hop_count × 1000,也就是跳数乘 1000。不能把它误写成时延感知或带宽感知路由。
6.7 LFA 备份
LFA(Loop-Free Alternate,无环路备选)是在主下一跳失效时可用且不会把包送回 环路的另一个下一跳。对源 S、候选邻居 N 和目的 D,常用判断是:
D(N,D) < D(N,S) + D(S,D)
其中 D(X,Y) 表示 X 到 Y 的图距离。本项目每个节点保存满足条件的第二下一跳;邻居失效时只切换受影响路由,不让一个节点失败导致全系统退出。
6.8 路由不等于 SR Policy
普通 FIB 回答“当前目的 IPv6 下一跳是谁”。SR Policy 回答“这类业务必须依次执行哪些segment”。SR Policy 中的每个 SID 仍需要普通 IPv6 FIB 把它送向当前执行位置。
因此:
SR Policy给出指令顺序
IPv6 FIB完成每条指令之间的逐跳可达
二者缺一不可。
6.9 第一编自测
读者应当能不看答案解释:
- 最终目的 IP 为什么与当前一跳 MAC 不是同一个层次?
- FIB 和邻居表各自把什么映射成什么?
- 为什么最长前缀匹配会选择
/128而不是/48? - 控制器持有拓扑为什么不等于控制器必须替节点安装 FIB?
- SRv6 封装 IPv4 为什么不叫地址族转换?