☰
P4环境搭建避坑指南:bmv2与protobuf/thrift版本编译实战
2026/10/21 8:20:51 网站建设 项目流程

简介:这份资源面向正在搭建P4可编程数据平面实验环境的学习者与研究人员,尤其适合需要一次性配齐behavioral-model、p4c及配套依赖的开发者。包内整合了bmv2行为模型、p4c编译器、gmock-1.7.0、protobuf-3.2.0与thrift-0.9.2等关键组件,可解决P4开发环境依赖版本繁杂、逐个编译耗时的问题,帮助读者快速进入P4语言与数据平面编程的实践环节。压缩包为gz格式,整体约147.8MB,涵盖源码与依赖库等主要文件类型,便于按组件解压安装。目前已有609人学习下载,说明该组合在P4入门与实验场景中具有一定参考价值。对于希望省去依赖查找与版本匹配环节、专注P4程序编写与验证的读者,这份安装包能提供较为完整的起步材料,配合相关配置教程可较快完成环境搭建并投入实验。

1. 从一台裸机到 bmv2 跑通:这套 P4 环境包到底省掉了哪些事

如果你在搜索 p4 bmv2 protobuf 这几个词,大概率已经经历过一次或多次编译失败。P4 语言本身不复杂,真正让人卡住的是它背后那套工具链:behavioral-model(bmv2)负责软件交换机行为模拟,p4c 负责把 P4 源码编译成 bmv2 能吃的 JSON,protobuf 和 thrift 负责进程间通信与运行时接口。这四个组件版本咬合极紧,protobuf 3.2.0 和 thrift 0.9.2 属于“过了这村没这店”的搭配,换一个版本,bmv2 的configure就可能直接报找不到符号。

这套安装包把 behavioral-model、gmock-1.7.0、p4c、protobuf-3.2.0、thrift-0.9.2 五个源码包按依赖顺序整理在一起,解决的就是“版本对不上、编译顺序错、依赖缺失”这三类反复翻车的问题。适合两类人:一是刚接触 P4、想在本机把 bmv2 跑起来做实验的新手;二是被环境折腾烦了、只想拿一套能复现的版本组合快速搭起实验环境的从业者。下面按依赖顺序拆开讲,每一步都给出可抄的命令和参数说明。

2. 依赖链拆解:为什么必须先 protobuf 再 thrift 最后 bmv2

2.1 五个包的依赖关系与编译顺序

这套环境的核心矛盾在于:bmv2 同时依赖 protobuf、thrift 和 gmock,而 p4c 又依赖 protobuf 和 thrift 的特定版本。如果顺序错了,典型现象是编译 bmv2 时提示undefined reference to google::protobuf::...,或者 thrift 生成的代码和 bmv2 期望的接口对不上。

正确的编译顺序是:

  1. protobuf-3.2.0(提供 libprotobuf 和 protoc)
  2. thrift-0.9.2(依赖 protobuf 的部分可选,但 bmv2 的运行时接口依赖它)
  3. gmock-1.7.0(bmv2 单元测试依赖,不装也能编,但建议装上)
  4. behavioral-model(bmv2 本体)
  5. p4c(P4 编译器,依赖 protobuf 和 thrift)

这个顺序不是拍脑袋定的,而是由configure脚本的检测逻辑决定的。protobuf 装完后要确保protoc --version输出libprotoc 3.2.0,thrift 装完后要确保thrift --version输出Thrift version 0.9.2,否则后面 bmv2 的configure会静默跳过某些模块,编出来的 bmv2 缺功能。

2.2 protobuf-3.2.0 编译:参数与安装路径

protobuf 是整个链条的地基,它装歪了后面全歪。我一般会把它装到/usr/local下,避免和系统自带的旧版本冲突。

# 解压 protobuf-3.2.0 tar -xzf protobuf-3.2.0.tar.gz cd protobuf-3.2.0 # 生成 configure 脚本,--prefix 指定安装路径 ./autogen.sh # 配置:关闭 64 位文件偏移检测,避免某些系统上编译报错 ./configure --prefix=/usr/local CXXFLAGS="-fPIC" --disable-shared # 编译并安装,-j4 按 CPU 核数调整 make -j4 sudo make install # 刷新动态库缓存,否则运行时会提示找不到 libprotobuf sudo ldconfig

这里有几个参数值得说清楚。CXXFLAGS="-fPIC"是为了生成位置无关代码,bmv2 链接静态库时如果没加这个,会报relocation R_X86_64_32 against ... can not be used when making a shared object。--disable-shared只编静态库,减少运行时动态库路径问题,但如果你后续要用 Python 的 protobuf 模块,可以改成--enable-shared。ldconfig这步很多人会漏,结果protoc能跑但程序运行时找不到库。

装完后验证:

protoc --version # 期望输出:libprotoc 3.2.0

如果输出的是系统自带的 2.x 版本,说明/usr/local/bin没在PATH最前面,需要手动调整。

2.3 thrift-0.9.2 编译:避开 C++ 编译器的坑

thrift 0.9.2 是个老版本,用新编译器(GCC 7 以上)编会报一堆throw()相关的错误。常见做法是加-std=c++03或者打补丁,但最省事的还是用 GCC 5 或 6 来编。

tar -xzf thrift-0.9.2.tar.gz cd thrift-0.9.2 # 只编 C++ 和 Python 支持,关掉其他语言减少编译量 ./configure --prefix=/usr/local --with-cpp --with-python --without-java --without-php # 如果编译器版本较高,加上兼容标志 make -j4 CXXFLAGS="-std=c++03 -fpermissive" sudo make install sudo ldconfig

--without-java --without-php这些不是必须的,但能显著缩短编译时间。-fpermissive是让编译器把一些错误降级为警告,thrift 0.9.2 的代码里有些旧式写法在新编译器下会直接报错。装完验证:

thrift --version # 期望输出:Thrift version 0.9.2

2.4 gmock-1.7.0 与 bmv2 的衔接

gmock-1.7.0 是 bmv2 单元测试用的,如果你不跑make check,其实可以不装。但既然安装包里带了,建议按顺序装上,避免后面想跑测试时再回头补。

tar -xzf gmock-1.7.0.tar.gz cd gmock-1.7.0 # gmock 1.7.0 自带 gtest,直接 make 即可 make -j4 sudo cp -r include/gmock /usr/local/include/ sudo cp -r include/gtest /usr/local/include/ sudo cp lib/.libs/libgmock.a /usr/local/lib/ sudo cp lib/.libs/libgtest.a /usr/local/lib/ sudo ldconfig

gmock 1.7.0 没有configure脚本,直接make就行。注意它编出来的库在lib/.libs/下,不是lib/下,复制路径别搞错。

3. bmv2 与 p4c 编译:从源码到可执行文件

3.1 behavioral-model 编译:configure 参数详解

bmv2 的编译是整个流程里最容易出问题的一步,因为它要同时检测 protobuf、thrift、gmock 三个依赖。如果前面装歪了,这里会直接暴露。

tar -xzf behavioral-model.tar.gz cd behavioral-model # 生成 configure 脚本 ./autogen.sh # 关键配置:指定 thrift 和 protobuf 的路径 ./configure --prefix=/usr/local \ --with-thrift=/usr/local \ --with-protobuf=/usr/local \ --with-gmock=/usr/local make -j4 sudo make install sudo ldconfig

--with-thrift、--with-protobuf、--with-gmock这三个参数如果不指定,configure会去默认路径找,但如果你把库装在/usr/local而系统路径里还有旧版本,就可能找到错的。显式指定最稳妥。

编译完成后,bmv2 的核心可执行文件是simple_switch和simple_switch_CLI。验证:

simple_switch --help # 应该输出 usage 信息,而不是报找不到库

如果报error while loading shared libraries: libthrift-0.9.2.so,说明ldconfig没生效或者安装路径不在默认搜索路径里,检查/etc/ld.so.conf.d/下有没有包含/usr/local/lib。

3.2 p4c 编译:依赖管理与后端选择

p4c 是 P4 编译器,它把.p4源码编译成 bmv2 能加载的 JSON 文件。p4c 的依赖比 bmv2 更多,除了 protobuf 和 thrift,还需要 boost、gc 等库。

# 先装系统依赖(以 Debian/Ubuntu 为例) sudo apt-get install -y g++ git automake libtool libboost-dev \ libboost-system-dev libboost-thread-dev libgc-dev bison flex tar -xzf p4c.tar.gz cd p4c # 初始化子模块(如果源码包带了子模块) git submodule update --init --recursive ./autogen.sh ./configure --prefix=/usr/local \ --with-protobuf=/usr/local \ --with-thrift=/usr/local make -j4 sudo make install sudo ldconfig

p4c 编译时间较长,-j4可以按机器配置调整。装完后验证:

p4c --version # 应该输出版本信息 p4c-bm2-ss --help # 确认 bmv2 后端可用

p4c-bm2-ss是专门给 bmv2 生成 JSON 的后端,如果这个命令不存在,说明编译时没启用 bmv2 后端,需要检查configure输出里bmv2那一项是不是yes。

3.3 一个最小 P4 程序的编译与加载

环境装好后,用一个最小程序验证整条链路。新建test.p4:

#include <core.p4> #include <v1model.p4> header ethernet_t { bit<48> dstAddr; bit<48> srcAddr; bit<16> etherType; } struct metadata { } parser MyParser(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { state start { packet.extract(hdr.ethernet); transition accept; } } control MyIngress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { apply { } } control MyEgress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) { apply { } } control MyDeparser(packet_out packet, in headers hdr) { apply { packet.emit(hdr.ethernet); } } V1Switch(MyParser(), MyIngress(), MyEgress(), MyDeparser()) main;

编译:

p4c-bm2-ss --p4v 1.6 test.p4 -o test.json

--p4v 1.6指定 P4 语言版本,-o指定输出 JSON 文件。如果编译成功,会生成test.json。然后用 bmv2 加载:

sudo simple_switch -i 0@veth0 -i 1@veth1 test.json

-i 0@veth0表示把端口 0 绑定到veth0接口。这需要提前创建好 veth pair,属于网络命名空间的操作,不在本文展开。加载成功后,simple_switch会进入交互模式,可以用simple_switch_CLI连接查看表项。

4. 避坑与排查:五个真实翻车记录

4.1 protoc 版本不对导致 bmv2 编译失败

现象:bmv2make时报undefined reference to google::protobuf::internal::...,或者protoc生成的.pb.cc文件编译报错。

原因:系统里存在多个 protobuf 版本,configure找到的是旧版头文件,但链接的是新版库,或者反过来。

解决:用which protoc和protoc --version确认当前用的是 3.2.0。如果不是,在configure时显式加--with-protobuf=/usr/local,并在make前执行export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH。

4.2 thrift 编译时 C++11 相关报错

现象:thrift 0.9.2make时报error: 'throw()' is deprecated或error: no matching function for call to ...。

原因:GCC 7 及以上默认用 C++14/17,thrift 0.9.2 的代码是按 C++03 写的。

解决:make时加CXXFLAGS="-std=c++03 -fpermissive"。如果还报错,考虑用 GCC 5 编译 thrift,或者找社区补丁。

4.3 bmv2 运行时找不到 libthrift

现象:simple_switch执行时报error while loading shared libraries: libthrift-0.9.2.so: cannot open shared object file。

原因:thrift 装在/usr/local/lib,但该路径不在动态链接器的默认搜索路径里。

解决:执行sudo ldconfig,或者手动把/usr/local/lib加到/etc/ld.so.conf.d/local.conf里再ldconfig。临时方案是export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH。

4.4 p4c 编译时找不到 boost 库

现象:p4cconfigure时报boost/... header not found或cannot find -lboost_system。

原因:boost 开发包没装全,或者版本太老。

解决:sudo apt-get install libboost-dev libboost-system-dev libboost-thread-dev。如果系统源里的 boost 版本低于 1.60,需要手动编译新版 boost,或者换用更新的系统版本。

4.5 simple_switch 加载 JSON 时报解析错误

现象:simple_switch test.json报Error while parsing JSON或Invalid header type。

原因:p4c 编译时用的 P4 语言版本和 bmv2 期望的不一致,或者 JSON 文件不完整。

解决:确认p4c-bm2-ss --p4v 1.6里的版本号和 bmv2 支持的版本匹配。bmv2 对 P4 1.6 的支持最好,尽量用 1.6。如果 JSON 文件是手动改过的,用python -m json.tool test.json检查格式是否合法。

5. 进阶验证:用 scapy 发包测试 bmv2 转发

环境跑通后,怎么确认 bmv2 真的在按 P4 逻辑转发?我一般用 scapy 构造数据包,从 veth 一端发进去,另一端抓包看结果。这个方法比单纯看simple_switch日志直观得多。

先创建一对 veth:

sudo ip link add veth0 type veth peer name veth1 sudo ip link set veth0 up sudo ip link set veth1 up

然后启动 bmv2,绑定端口:

sudo simple_switch -i 0@veth0 -i 1@veth1 test.json &

用 scapy 从veth1发包:

from scapy.all import Ether, sendp # 构造一个以太网帧,目的地址全 f,源地址随意 pkt = Ether(dst="ff:ff:ff:ff:ff:ff", src="00:11:22:33:44:55", type=0x0800) sendp(pkt, iface="veth1", count=3)

如果 P4 程序里配了转发逻辑(比如把端口 1 收到的包从端口 0 发出去),在veth0上抓包就能看到:

sudo tcpdump -i veth0 -e -c 5

-e显示以太网头部,-c 5抓 5 个包后退出。如果看到源 MAC 是00:11:22:33:44:55的帧,说明 bmv2 确实在转发。

这个验证方法的好处是能直接看到 P4 程序对包的处理结果,而不是只看编译是否通过。我习惯在改完 P4 逻辑后,先用 scapy 发几个特定包,确认转发、丢弃、修改头部这些行为符合预期,再去跑更复杂的测试用例。

从那以后我每次搭 P4 环境,都会先把 protobuf 和 thrift 的版本验证一遍,再按顺序编译,最后用 scapy 发一个包确认整条链路通了,才敢开始写业务逻辑。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询