本文是《从 IP 到 SRv6 计算编排》新手教材第 1/13 编。 总目录 · 下一篇:第二编:SRv6 与 SR Policy
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(解封装)**是接收端删除已经完成使命的外层头部。
例如,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(观察面)**记录事件、统计和图片,供分析与前端展示。
本项目的底线是:观察面故障不能停止数据面;控制面不能承载被验证的业务数据; 控制器不能代替节点完成本地转发决策。
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为什么不叫地址族转换?