土法炼钢兴趣小组的算法知识备份

【网络工程】可编程数据平面与 P4:软件定义转发

文章导航

分类入口
network
标签入口
#p4#programmable-dataplane#sdn#tofino#network-programming

目录

传统网络设备——交换机、路由器——的转发逻辑由芯片厂商在硬件设计时决定。你能配置的只有转发表的内容,不能改变转发的逻辑。P4(Programming Protocol-independent Packet Processors)改变了这一点:它让你用一门领域专用语言定义整个包处理管线——从包头解析到匹配-动作到重新序列化。

一、为什么需要可编程数据平面

1.1 固定功能交换机的局限

传统交换机的转发管线(固定功能):

  包到达 → 解析以太网头 → 解析 IP 头 → 解析 TCP/UDP 头
       → 查找 MAC 表 → 查找路由表 → 查找 ACL
       → 修改 TTL → 修改 MAC → 发送

问题:
1. 新协议支持需要等芯片厂商升级
   例:VXLAN 出现后,等了 2-3 年才有硬件支持

2. 自定义行为不可能
   例:想在交换机上做 INT(带内网络遥测)→ 不支持

3. 功能浪费
   例:你只需要 L2 转发,但芯片包含完整的 L3/ACL 管线

1.2 可编程 vs 固定功能

维度 固定功能交换机 可编程交换机
转发逻辑 硬件固化 P4 程序定义
新协议支持 等厂商升级 自己实现
功能灵活性 预设功能集 任意自定义
性能 线速 线速(ASIC)
成本 较低 较高
典型产品 Broadcom Memory Intel Tofino

二、P4 语言核心概念

2.1 P4 架构模型

P4 程序定义的包处理管线:

  ┌────────────┐   ┌─────────────────┐   ┌─────────────┐
  │   Parser   │ → │  Match-Action   │ → │  Deparser   │
  │  包头解析   │   │   匹配-动作表    │   │  包头重组    │
  └────────────┘   └─────────────────┘   └─────────────┘
       ↓                   ↓                    ↓
  从原始字节流        查找表并执行动作         将修改后的
  提取包头字段        (转发/丢弃/修改)       包头写回字节流

2.2 P4 程序结构

// P4_16 语言示例——简单的 L2 交换机

#include <core.p4>
#include <v1model.p4>

// 1. 定义包头(Header)
header ethernet_t {
    bit<48> dstAddr;
    bit<48> srcAddr;
    bit<16> etherType;
}

header ipv4_t {
    bit<4>  version;
    bit<4>  ihl;
    bit<8>  tos;
    bit<16> totalLen;
    bit<16> identification;
    bit<3>  flags;
    bit<13> fragOffset;
    bit<8>  ttl;
    bit<8>  protocol;
    bit<16> hdrChecksum;
    bit<32> srcAddr;
    bit<32> dstAddr;
}

struct headers {
    ethernet_t ethernet;
    ipv4_t     ipv4;
}

struct metadata { }

// 2. 定义解析器(Parser)
parser MyParser(packet_in packet,
                out headers hdr,
                inout metadata meta,
                inout standard_metadata_t standard_metadata) {

    state start {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.etherType) {
            0x0800: parse_ipv4;
            default: accept;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }
}

// 3. 定义匹配-动作表(Match-Action)
control MyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {

    action drop() {
        mark_to_drop(standard_metadata);
    }

    action forward(bit<9> port) {
        standard_metadata.egress_spec = port;
    }

    table l2_forward {
        key = {
            hdr.ethernet.dstAddr: exact;
        }
        actions = {
            forward;
            drop;
        }
        size = 1024;
        default_action = drop();
    }

    apply {
        l2_forward.apply();
    }
}

// 4. 定义反解析器(Deparser)
control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
    }
}

2.3 P4 匹配类型

匹配类型 含义 适用场景
exact 精确匹配 MAC 地址、端口号
lpm 最长前缀匹配 IP 路由
ternary 三态匹配(值/掩码) ACL
range 范围匹配 端口范围
optional 可选匹配 可选字段

三、可编程交换机硬件

3.1 Intel Tofino

# Intel Tofino(原 Barefoot Networks)
# 第一款商用可编程交换机芯片

# Tofino 1(2016)
# - 6.5 Tbps 吞吐量
# - 完全可编程的包处理管线
# - PISA 架构(Protocol Independent Switch Architecture)

# Tofino 2(2020)
# - 12.8 Tbps 吞吐量
# - 更多 ALU 和更大的 SRAM/TCAM
# - 支持更复杂的 P4 程序

# Tofino 3(取消)
# Intel 于 2023 年宣布停止 Tofino 芯片开发
# 但 P4 语言和生态继续发展

# 使用 Tofino 的产品:
# - Arista 7170 系列
# - Edgecore Wedge 系列
# - 各大云厂商自研交换机

3.2 PISA 架构

PISA(Protocol Independent Switch Architecture):

  ┌──────────┐   ┌─────────────────────────────────┐   ┌──────────┐
  │ Ingress  │   │     Match-Action Stages          │   │ Egress   │
  │ Parser   │ → │  ┌────┐ ┌────┐ ┌────┐ ┌────┐   │ → │ Deparser │
  │          │   │  │ S1 │→│ S2 │→│ S3 │→│...│   │   │          │
  └──────────┘   │  └────┘ └────┘ └────┘ └────┘   │   └──────────┘
                 └─────────────────────────────────┘

每个 Stage 包含:
├── Match 单元(TCAM + SRAM)
│   查找表,确定要执行的动作
├── Action 单元(ALU)
│   执行动作(修改字段、计算、计数)
└── 状态存储(Register / Counter / Meter)
    有状态操作(流计数、限速)

Tofino 1 有 12 个 ingress stage + 12 个 egress stage
每个 stage 可以并行处理多个表

四、P4 应用案例

4.1 带内网络遥测(INT)

// INT(In-band Network Telemetry)
// 让每个交换机在包经过时添加遥测数据

// 定义 INT 头部
header int_metadata_t {
    bit<32> switch_id;
    bit<32> ingress_port;
    bit<32> egress_port;
    bit<48> ingress_timestamp;
    bit<48> egress_timestamp;
    bit<32> queue_depth;
    bit<32> queue_latency;
}

// 在 egress 管线中插入遥测数据
control MyEgress(...) {
    action add_int_metadata() {
        hdr.int_metadata.setValid();
        hdr.int_metadata.switch_id = SWITCH_ID;
        hdr.int_metadata.ingress_port = 
            (bit<32>)standard_metadata.ingress_port;
        hdr.int_metadata.egress_port = 
            (bit<32>)standard_metadata.egress_port;
        hdr.int_metadata.ingress_timestamp = 
            standard_metadata.ingress_global_timestamp;
        hdr.int_metadata.queue_depth = 
            (bit<32>)standard_metadata.enq_qdepth;
    }
}

// 效果:每个包携带完整的路径信息
// 终端收集后可以还原:
// - 每跳延迟
// - 每跳队列深度
// - 完整路径

4.2 网络负载均衡

// P4 实现的负载均衡——线速处理

control LoadBalancer(...) {
    // 后端服务器表
    action set_backend(bit<32> dst_ip, bit<48> dst_mac, bit<9> port) {
        hdr.ipv4.dstAddr = dst_ip;
        hdr.ethernet.dstAddr = dst_mac;
        standard_metadata.egress_spec = port;
    }

    // 使用五元组哈希选择后端
    table backend_select {
        key = {
            hdr.ipv4.srcAddr: exact;
            hdr.ipv4.dstAddr: exact;
            hdr.ipv4.protocol: exact;
            hdr.tcp.srcPort: exact;
            hdr.tcp.dstPort: exact;
        }
        actions = {
            set_backend;
            drop;
        }
    }

    // 一致性哈希表
    action_selector(HashAlgorithm.crc32, 32w1024, 32w16) backend_selector;

    apply {
        backend_select.apply();
    }
}

4.3 SRv6——段路由

# P4 可以实现 SRv6 的线速处理
# SRv6(Segment Routing over IPv6)将路由信息编码在 IPv6 扩展头中

# 在 P4 中实现 SRv6:
# 1. Parser 解析 SRv6 Routing Header
# 2. Match-Action 查找下一个 Segment
# 3. 修改 IPv6 目的地址为下一 Segment
# 4. Deparser 重新组装包

# 优势:无需修改交换机固件即可支持 SRv6
# 运营商(如 Softbank、LINE)已在生产中使用

五、P4 开发与测试

5.1 开发工具链

# P4 开发环境

# 1. p4c — P4 编译器
# 将 P4 程序编译为目标平台的代码
p4c --target bmv2 --arch v1model program.p4

# 2. BMv2 — 软件交换机
# 用于开发和测试的 P4 软件交换机
simple_switch_grpc --device-id 0 -i 0@eth0 -i 1@eth1 program.json

# 3. Mininet — 网络模拟
# 创建包含 BMv2 交换机的虚拟网络
# 用于功能测试

# 4. P4Runtime — 控制平面 API
# 标准化的控制平面接口
# 用于下发转发表项

# 5. p4app — 一键测试环境
# Docker 化的 P4 开发环境

5.2 测试流程

# P4 程序的测试方法论

# 1. 单元测试——使用 STF(Simple Test Framework)
# 定义输入包和期望输出
# packet 0 00112233445566778899aabb0800...
# expect 1 00112233445566778899aabb0800...

# 2. 功能测试——使用 PTF(Packet Test Framework)
# Python 测试框架,发送真实包并验证
import ptf
from ptf.testutils import send_packet, verify_packet

class L2ForwardTest(ptf.BaseTest):
    def test_forward(self):
        pkt = simple_eth_packet(eth_dst='00:11:22:33:44:55')
        send_packet(self, port=0, pkt=pkt)
        verify_packet(self, pkt, port=1)

# 3. 性能测试——使用硬件目标
# 在 Tofino 交换机上加载程序
# 使用 IXIA/Spirent 流量发生器测试线速

六、P4 vs eBPF

维度 P4 eBPF
运行位置 交换机 ASIC Linux 内核
性能 线速(Tbps 级) 高(Gbps 级)
适用场景 数据中心网络交换 服务器网络处理
编程模型 声明式(表+动作) 命令式(C-like)
灵活性 包头解析全自定义 包处理逻辑自定义
状态支持 有限(寄存器/计数器) 丰富(Map)
调试 硬件调试困难 bpftool / bpftrace
生态 较小 活跃
# 选择建议:
# P4  — 需要线速处理 Tbps 级流量(交换机/路由器)
# eBPF — 需要灵活的服务器网络处理(容器网络/可观测性)
# 组合使用 — P4 做硬件转发 + eBPF 做主机端处理

七、P4 在数据中心的应用

7.1 数据中心网络遥测

# 传统网络监控——采样式(sFlow/NetFlow)
# 问题:采样率 1/1000,可能错过短暂异常
# P4 方案:每个包都携带遥测数据(INT)

# INT 部署模式:
# 1. 源节点——在包头插入 INT 头
# 2. 中间节点——追加本地遥测数据
# 3. 目的节点——提取遥测数据,发送到收集器

# 采集的遥测数据:
# - 交换机 ID
# - 入口/出口端口
# - 入口/出口时间戳(纳秒精度)
# - 队列深度
# - 队列等待时间
# - 链路利用率

# 使用场景:
# 1. 微突发检测——传统采样无法检测 μs 级别的队列膨胀
# 2. 精确延迟定位——知道延迟发生在哪个交换机
# 3. 路径验证——确认流量走了预期的路径
# 4. 拥塞定位——精确到队列级别的拥塞检测

7.2 网络切片

// P4 实现网络切片——不同租户使用不同的转发管线

// 定义切片标识头
header slice_header_t {
    bit<16> slice_id;
    bit<8>  priority;
    bit<8>  reserved;
}

control SliceRouter(inout headers hdr, ...) {
    // 每个切片有独立的转发表
    table slice_forward {
        key = {
            hdr.slice.slice_id: exact;
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            forward;
            drop;
        }
    }

    // 每个切片有独立的限速器
    direct_meter<bit<2>>(MeterType.bytes) slice_meter;

    table slice_rate_limit {
        key = {
            hdr.slice.slice_id: exact;
        }
        actions = {
            set_meter;
        }
        meters = slice_meter;
    }

    apply {
        slice_rate_limit.apply();
        // 检查速率限制结果
        if (slice_meter_result == 2) {  // RED
            drop();
        } else {
            slice_forward.apply();
        }
    }
}

7.3 自定义协议支持

# P4 最大的价值——快速支持新协议

# 案例:支持自定义隧道协议
# 传统方式:等厂商在下一代芯片中支持(2-3 年)
# P4 方式:写 P4 程序,当天部署(小时级)

# 案例:Segment Routing v6
# 2018 年 SRv6 标准化时,P4 交换机即可支持
# 传统交换机要等到 2020 年才有硬件支持

# 案例:自定义负载均衡头
# 云厂商设计的内部封装协议
# 无需等待芯片厂商支持
# P4 直接实现,线速处理

八、P4Runtime 控制平面

8.1 P4Runtime 架构

┌─────────────────┐
│   控制器         │
│  (ONOS / SDN)   │
└────────┬────────┘
         │ P4Runtime gRPC
         │ (表项下发/读取)
         │
┌────────┴────────┐
│   P4 交换机      │
│  ┌──────────┐   │
│  │ P4 程序   │   │
│  │ (数据面)  │   │
│  └──────────┘   │
└─────────────────┘

8.2 P4Runtime 操作

# 使用 Python 控制 P4 交换机

import p4runtime_sh.shell as sh

# 连接交换机
sh.setup(
    device_id=0,
    grpc_addr='localhost:9559',
    election_id=(0, 1),
    config=sh.FwdPipeConfig('program.p4info.txt', 'program.json')
)

# 插入转发表项
te = sh.TableEntry('l2_forward')(action='forward')
te.match['hdr.ethernet.dstAddr'] = '00:11:22:33:44:55'
te.action['port'] = '1'
te.insert()

# 读取表项
for te in sh.TableEntry('l2_forward').read():
    print(te)

# 删除表项
te = sh.TableEntry('l2_forward')(action='forward')
te.match['hdr.ethernet.dstAddr'] = '00:11:22:33:44:55'
te.delete()

# 读取计数器
for ce in sh.CounterEntry('packet_counter').read():
    print(f'Counter {ce.index}: {ce.packet_count} pkts, {ce.byte_count} bytes')

九、P4 与 SmartNIC

9.1 SmartNIC 上的 P4

# SmartNIC(智能网卡)是 P4 的另一个重要目标平台
# 在网卡上运行 P4 程序,卸载主机 CPU 的网络处理

# 支持 P4 的 SmartNIC:
# 1. AMD Pensando DSC(P4 原生支持)
# 2. NVIDIA BlueField(通过 DOCA SDK)
# 3. Intel IPU(Infrastructure Processing Unit)

# SmartNIC P4 vs 交换机 P4:
# 交换机:Tbps 级,纯硬件管线
# SmartNIC:100Gbps 级,可能包含 ARM 核心辅助

# 典型应用:
# 1. OVS 卸载——将 Open vSwitch 的转发逻辑卸载到 SmartNIC
# 2. 存储网络——NVMe-oF 的 RDMA 处理
# 3. 安全——线速加密/解密
# 4. 遥测——在网卡上采集 INT 数据

9.2 DPDK vs SmartNIC P4

维度 DPDK(CPU) SmartNIC P4
位置 主机 CPU 网卡
CPU 开销 占用多个核心 零 CPU 开销
性能 ~40 Mpps/核 ~200 Mpps
灵活性 完全灵活 P4 模型内灵活
功耗 高(CPU 轮询) 低(硬件处理)
适用 中等流量 高流量

十、P4 生态现状与展望

10.1 当前挑战

# P4 面临的工程挑战:

# 1. 硬件可用性
# Intel 停止 Tofino 开发后,可编程交换机的未来有不确定性
# 但 AMD/Pensando 和其他厂商在接力
# SmartNIC 市场在快速增长

# 2. 人才稀缺
# P4 程序员远比传统网络工程师少
# 学习曲线陡峭——需要同时理解网络和硬件

# 3. 调试困难
# 硬件上的 P4 程序调试工具不够成熟
# 软件模拟器和硬件行为可能不一致
# 时序相关的 bug 难以复现

# 4. 芯片资源限制
# TCAM/SRAM 容量有限
# Stage 数量有限——Tofino 12 个 stage
# 复杂程序可能无法放入硬件管线
# 需要精心优化资源使用

# 5. 生态碎片化
# 不同厂商的 P4 架构模型不同
# 可移植性有限(v1model vs PSA vs TNA)

10.2 未来方向

方向 说明 时间线
SmartNIC P4 在网卡上运行 P4 程序 已有产品
P4 + eBPF 融合 P4 编译为 eBPF 字节码 研究中
标准化 P4 架构 PSA(Portable Switch Architecture) 进行中
AI 辅助编程 自动优化资源分配 研究中
网络数字孪生 P4 模拟真实网络 研究中
# P4 编译到 eBPF——p4c-ebpf
# 将 P4 程序编译为 eBPF C 代码
# 然后编译为 eBPF 字节码加载到 Linux 内核
p4c --target ebpf --arch ebpf program.p4

# 优势:不需要可编程硬件
# 劣势:性能不如 ASIC(Gbps vs Tbps)
# 适用:开发测试、小规模部署

十一、云厂商的 P4 实践

11.1 Google

# Google 是 P4 的重要推动者
# 内部交换机使用可编程芯片

# 案例:Orion SDN
# - 使用 P4 定义数据中心交换机的转发管线
# - 控制面由 Orion SDN 控制器管理
# - 支持自定义封装协议和遥测

# 案例:Andromeda 虚拟网络
# - 虚拟交换机部分逻辑卸载到可编程网卡
# - P4 程序处理 GUE 封装/解封装

11.2 Microsoft

# Microsoft Azure 使用 FPGA 实现类 P4 的可编程数据平面

# 案例:Azure Accelerated Networking
# - FPGA 网卡卸载虚拟网络处理
# - 类 P4 的编程模型定义转发逻辑
# - 减少主机 CPU 开销,降低网络延迟

# 案例:Azure SmartNIC
# - FPGA 实现 SDN 数据平面
# - 支持 VXLAN、GRE 等隧道协议
# - 线速 ACL 和 NAT 处理

11.3 AWS

# AWS 使用自研网络芯片

# 案例:AWS Nitro
# - 自研 SmartNIC/DPU
# - 卸载 VPC 网络处理
# - 硬件实现 EBS 存储网络

# 案例:SRD(Scalable Reliable Datagram)
# - AWS 自研的类 QUIC 传输协议
# - 在 Nitro 卡上硬件实现
# - 低延迟、高吞吐的数据中心传输

十二、P4 学习路径

12.1 入门路径

# 阶段 1:理解基础概念(1-2 周)
# - 阅读 P4_16 语言规范
# - 理解 Parser → Match-Action → Deparser 模型
# - 完成 P4 官方教程

# 阶段 2:动手实践(2-4 周)
# - 安装 p4c + BMv2 开发环境
# - 实现 L2 交换机
# - 实现 L3 路由器
# - 实现简单的负载均衡器
# - 学习 P4Runtime API

# 阶段 3:进阶应用(4-8 周)
# - 实现 INT 遥测
# - 实现 SRv6
# - 学习 Tofino 架构(如果有硬件)
# - 资源优化和性能调优

# 推荐资源:
# - P4 官方教程:https://github.com/p4lang/tutorials
# - P4 语言规范:https://p4.org/specs/
# - Stanford CS344:Building an Internet Router

12.2 开发环境搭建

# 使用 Docker 快速搭建 P4 开发环境

# 方法 1:使用官方 Docker 镜像
docker pull p4lang/p4c
docker pull p4lang/behavioral-model

# 方法 2:使用 p4app
git clone https://github.com/p4lang/p4app.git
cd p4app
./p4app run examples/simple_router.p4app

# 方法 3:从源码编译
git clone https://github.com/p4lang/p4c.git
cd p4c
mkdir build && cd build
cmake ..
make -j$(nproc)

# 验证安装
p4c --version
simple_switch_grpc --version

12.3 P4 与传统网络工程师

# P4 对传统网络工程师的意义:

# 传统网络工程师的工作:
# - 配置交换机/路由器的 CLI
# - 设计 VLAN/OSPF/BGP 参数
# - 排查 STP/ARP 等协议问题

# P4 时代的网络工程师:
# - 编写 P4 程序定义转发逻辑
# - 使用 P4Runtime API 管理数据平面
# - 设计和实现自定义网络功能
# - 兼具网络知识和编程能力

# 转型建议:
# 1. 学习编程基础(Python/C)
# 2. 理解软件定义网络(SDN)概念
# 3. 学习 P4 语言
# 4. 实践——从软件交换机开始

12.4 P4 程序调试技巧

# BMv2 调试功能

# 启用日志——查看每个包的处理过程
simple_switch_grpc --log-console --log-level debug program.json

# 日志输出示例:
# [10:23:45.123] [bmv2] [D] Packet in on port 0
# [10:23:45.123] [bmv2] [D] Parser 'parser': parsing header 'ethernet'
# [10:23:45.123] [bmv2] [D] Table 'l2_forward': key = {dstAddr: 00:11:22:33:44:55}
# [10:23:45.123] [bmv2] [D] Table 'l2_forward': hit, entry 0
# [10:23:45.123] [bmv2] [D] Action forward: port=1

# 使用 Nanomsg 接口监控
# BMv2 支持 IPC 接口,可以实时监控包处理
simple_switch_grpc --nanolog ipc:///tmp/bm-log.ipc program.json

# 使用 P4 断言调试
# 在 P4 代码中插入 assert 检查假设
control MyIngress(...) {
    apply {
        // 确保 TTL 不为 0
        assert(hdr.ipv4.ttl > 0);
    }
}

# Tofino 硬件调试
# 使用 bfrt_python CLI 交互式检查表项
bfrt_python
bfrt> bfrt.program.pipe.l2_forward.dump(table=True)

参考文献

  1. Bosshart, P. et al., “P4: Programming Protocol-Independent Packet Processors,” ACM SIGCOMM CCR, 2014.
  2. P4 Language Consortium, “P4_16 Language Specification,” p4.org.
  3. McKeown, N. et al., “OpenFlow: Enabling Innovation in Campus Networks,” ACM SIGCOMM CCR, 2008.
  4. Sivaraman, A. et al., “Programmable Packet Scheduling at Line Rate,” ACM SIGCOMM, 2016.
  5. Kim, C. et al., “In-band Network Telemetry via Programmable Dataplanes,” ACM SIGCOMM Demo, 2015.
  6. Intel, “Tofino Series Programmable Ethernet Switch ASIC,” intel.com.

上一篇: eBPF 可编程网络:从包过滤到流量工程 下一篇: 网络模拟与测试:netem、Mininet 与混沌工程

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-07-29 · network

【网络工程】网络隔离与微分段:VLAN、SDN 策略与零信任

网络隔离是安全架构的基石。本文从传统 VLAN 的 4096 限制、VXLAN 的 Overlay 隔离机制、SDN 下的 Calico/Cilium Network Policy 工程实践、微分段的设计方法论,到零信任网络架构的分段策略,系统讲解从物理隔离到软件定义隔离的演进和工程落地。

2025-07-26 · network

【网络工程】网络 I/O 模式:Reactor、Proactor 与协程

Reactor 和 Proactor 是网络服务器的两种核心 I/O 处理模式。本文从单线程 Reactor、多线程主从 Reactor、Proactor 与 io_uring 的天然契合,到 Go goroutine、Rust async 和 Java Virtual Thread 的协程网络 I/O 对比,系统分析各模式的适用场景与工程权衡。

2025-07-25 · network

【网络工程】UDP 工程:何时用 UDP、怎么用好 UDP

UDP 不是'不可靠的 TCP',它是一张白纸——不做连接管理、不做流控、不做重传,把所有决策权交给应用层。本文从 UDP 的协议本质出发,剖析它在游戏、视频、DNS、QUIC 等场景中的工程用法,深入分析 MTU/分片/NAT 穿越等工程陷阱,并给出高性能 UDP 编程的内核调优方法。


By .