C COMPIN BLOG

COMPIN TECHNICAL NOTE

新手教材(1/13)|第一编:从比特到 IPv6 路由

从 bit、byte、Ethernet 和 IPv4 开始,逐步理解 IPv6、邻居表、FIB 与逐跳路由。

本文是《从 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 校验题

  1. 一个字节有多少bit,能表示多少个不同值?
  2. 对Ethernet层而言,整个IPv6包属于header还是payload?
  3. 封装与修改内层数据有什么区别?

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应用数据为例:

  1. 应用层说明数据对业务意味着什么;
  2. UDP用端口把它交给目的进程;
  3. IP决定它跨越多跳后到哪个地址;
  4. Ethernet用下一跳MAC完成当前一跳;
  5. 真实链路决定这一跳是否连通、能以多快速度传输。

如果链路层开始解释图片含义,或者控制服务替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范围, 便于在封闭仿真中定义稳定地址合同。

**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 Address128-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不是“新旧开关”

对比IPv4IPv6
地址长度32 bit128 bit
常用写法点分十进制冒号分隔十六进制
邻居解析ARPNDP/ICMPv6
跳数限制TTLHop Limit
扩展机制基本头选项能力有限Extension Header链
本项目核心SRv6可作为内层业务作为外层核心和内层业务

SRv6依赖IPv6外层,但它可以承载IPv4或IPv6内层业务。

5.10 练习

  1. 展开fd42:1::a为8组十六进制数。
  2. /128和/48哪个匹配范围更精确?
  3. fe80::2为什么通常必须与接口名一起使用?
  4. 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 第一编自测

读者应当能不看答案解释:

  1. 最终目的IP为什么与当前一跳MAC不是同一个层次?
  2. FIB和邻居表各自把什么映射成什么?
  3. 为什么最长前缀匹配会选择/128而不是/48?
  4. 控制器持有拓扑为什么不等于控制器必须替节点安装FIB?
  5. SRv6封装IPv4为什么不叫地址族转换?

总目录 · 下一篇:第二编:SRv6 与 SR Policy