数据平面:一个 IP 包的完整旅程
专栏:VNT 2.0 组网源码 · 第 7 / 11 篇:::info 学习目标 完成本篇后你能够:按顺序说出出站流水线的五道工序及其理由;解释 FEC 的分批与恢复机制;说出广播包的三种发送形态。 前置:第 4~6 篇完成。预计时长:60 分钟。 :::
:::note 本章术语速查(新手建议先读)
- 数据平面 / 控制平面:数据平面=搬运用户流量的路径;控制平面=协调决策(注册、打洞、路由)。
- LZ4:极快的压缩算法(压缩率一般但速度极快)。
- FEC(前向纠错):附带“数学纠错包”,丢了部分包可直接还原——不用重传。
- Reed-Solomon:经典纠错编码(CD/DVD 也用它)。
- 广播 / 单播:广播=发给所有人;单播=发给某一个。 :::
前几篇拆了各个零件,这篇把它们串成流水线——TUN 读出的每个 IP 包,到进入网络前要过五道工序。
出站流水线
源码主链(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 参数)让”虚拟网外的地址”也能被转发给网关型设备——跨网段访问的实现。
入站:对称的逆序
入站同样带”帮忙转发”:收到非本包目的的包时,只要 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) 评分选一台服务器负责扩散——避免每台服务器都转发一份造成重复。
常见踩坑
- 开了 FEC 感觉更慢——FEC 换的是丢包容忍不是带宽;网络好时别开;
- 压缩对小包无效——<256 字节直接跳过(压了也不小);
- 跨网段不通——检查网关设备的
input/output参数与系统转发开关(ip_forward)。
随堂练习(带验收标准)
- 开启
--fec --compress后抓包,对比开关前后物理网卡上的包大小与数量; - 用日志验证:一条 100 字节的小包是否跳过了压缩(<256 阈值);
- 进阶:读
fec/decoder.rs,计算”10 包丢 3 个但收到 2 个校验包”时的恢复条件。