数据平面:一个 IP 包的完整旅程

专栏:VNT 2.0 组网源码 · 第 7 / 11 篇
VNT数据平面FEC

:::info 学习目标 完成本篇后你能够:按顺序说出出站流水线的五道工序及其理由;解释 FEC 的分批与恢复机制;说出广播包的三种发送形态。 前置:第 4~6 篇完成。预计时长:60 分钟。 :::

:::note 本章术语速查(新手建议先读)

  • 数据平面 / 控制平面:数据平面=搬运用户流量的路径;控制平面=协调决策(注册、打洞、路由)。
  • LZ4:极快的压缩算法(压缩率一般但速度极快)。
  • FEC(前向纠错):附带“数学纠错包”,丢了部分包可直接还原——不用重传。
  • Reed-Solomon:经典纠错编码(CD/DVD 也用它)。
  • 广播 / 单播:广播=发给所有人;单播=发给某一个。 :::

前几篇拆了各个零件,这篇把它们串成流水线——TUN 读出的每个 IP 包,到进入网络前要过五道工序。

出站流水线

图表(vnt-data-plane.md)

源码主链(tunnel_core/outbound.rs:197-236):

// vnt-core/src/tunnel_core/outbound.rs:197-236(节选)
pub async fn ipv4_outbound(&self, net: NetworkAddr, mut data: TransmissionBytes) -> anyhow::Result<()> {
    let ipv4 = Ipv4Packet::new(data.as_ref()) ...;
    data.retreat_head(HEAD_LENGTH)?;              // 零拷贝退指针加头(第 4 篇)
    let mut packet = NetPacket::new(data)?;
    packet.set_msg_type(MsgType::Turn);
    packet.set_ttl(5);
    if !net.network().contains(&dest) {           // 不在虚拟网段 → 查子网路由
        if let Some(v) = self.external_route.route(&dest) { dest = v; }
        else { return Ok(()); }
    }
    packet = self.packet_compression.compress(packet, ...)?;   // LZ4
    if let Some(fec_encoder) = &self.fec_encoder {
        packet = fec_encoder.encode(packet)?;      // Reed-Solomon
    }
    self.basic_outbound.send_encrypted_packet(dest, packet).await?;

顺序的理由:压缩必须在加密前(密文不可压);FEC 在加密前(冗余校验包也要被加密);加密在最后(出网卡前一刻)。子网路由(input 参数)让”虚拟网外的地址”也能被转发给网关型设备——跨网段访问的实现。

入站:对称的逆序

图表(vnt-data-plane.md)

入站同样带”帮忙转发”:收到非本包目的的包时,只要 ttl≥1 且自己有到目的地的路由就转发——这就是第 6 篇三层中继的第二层。

FEC:用 20% 带宽换丢包容忍

FEC(前向纠错)默认关闭(--fec 开启),机制(fec/encoder.rs):

  • 每个目的 IP 独立分批:10 包一批或 20ms 超时即封批;
  • Reed-Solomon 编码生成 ceil(10×20%)=2 个校验包(至少 1 个);
  • 数据包与校验包都封装成 protobuf FecPacket{group_id, packet_index, payload, parity_data};
  • 解码端按 (源, 组) 聚合(组超时 3 秒、上限 1000 组):只要收到 10 个中的任意 10 个分片(数据或校验),即可还原全部——丢 1~2 个包无需重传。

适用场景:UDP 被运营商丢包、实时性敏感(游戏/语音)宁可多花 20% 带宽也不想重传。与 QUIC 可靠通道(rtx)互补:FEC 管”少量丢包容忍”,QUIC 管”必须可靠”。

广播的三种形态

MsgType语义发送方式
Broadcast全员广播P2P 直发上限 16 个对端,其余经服务器 ExcludeBroadcast
ExcludeBroadcast除指定 IP 外广播SelectiveBroadcast{ips[], data}
TargetBroadcast定向广播到指定列表同上

多服务器时广播还要选主:按 ips/(rtt+1) 评分选一台服务器负责扩散——避免每台服务器都转发一份造成重复。

常见踩坑

  1. 开了 FEC 感觉更慢——FEC 换的是丢包容忍不是带宽;网络好时别开;
  2. 压缩对小包无效——<256 字节直接跳过(压了也不小);
  3. 跨网段不通——检查网关设备的 input/output 参数与系统转发开关(ip_forward)。

随堂练习(带验收标准)

  1. 开启 --fec --compress 后抓包,对比开关前后物理网卡上的包大小与数量;
  2. 用日志验证:一条 100 字节的小包是否跳过了压缩(<256 阈值);
  3. 进阶:读 fec/decoder.rs,计算”10 包丢 3 个但收到 2 个校验包”时的恢复条件。

← 返回文章列表