☰
P4开发环境搭建:bmv2与p4c完整安装指南
2026/10/21 13:48:46 网站建设 项目流程

简介:这份资源面向SDN与可编程数据平面方向的学习者与研究者,聚焦P4开发环境的完整搭建,解决behavioral-model、p4c、protobuf、thrift等组件版本匹配繁琐、编译依赖易出错的问题。包内整合了behavioral-model、gmock-1.7.0、p4c、protobuf-3.2.0与thrift-0.9.2等关键源码包,压缩包为gz格式,整体约147.8MB,适合需要一次性配齐P4工具链、避免逐一下载与版本冲突的中高级读者。目前已有609人学习下载,说明该组合在P4实验与课程实践中具有一定参考价值。借助这些组件,读者可完成bmv2软件交换机编译、p4c编译器构建及依赖库安装,为后续编写与验证P4程序、开展数据平面实验打下环境基础,同时减少因依赖缺失或版本不兼容导致的反复排错,提升环境部署效率。

1. P4 开发环境搭建:从零跑通 bmv2 + p4c 的完整安装路径

如果你正在搜 P4 环境怎么装,大概率已经体会过那种“每个组件单独看都能装,凑一起就互相打架”的无力感。P4 这套工具链不像普通 Python 包那样一条pip install就能收工,它由 behavioral-model(bmv2)、p4c 编译器、protobuf、thrift、gmock 等多个组件咬合而成,版本错一个,编译就报一屏看不懂的错。我见过太多人卡在configure: error或者undefined reference上,折腾两三天最后放弃。这篇笔记记录的是我实际跑通的一套组合:bmv2 + gmock-1.7.0 + p4c + protobuf-3.2.0 + thrift-0.9.2,从依赖准备到编译验证,每一步都给出可抄的命令和参数说明,新手照着走能通,熟手能看到版本取舍的坑。

2. 环境准备与版本选型:为什么这几个版本必须锁死

2.1 组件依赖关系先理清楚

P4 环境不是单一软件,而是一条编译链。bmv2 是软件交换机,负责实际转发行为;p4c 是编译器,把 P4 源码编译成 bmv2 能吃的 JSON;protobuf 负责序列化,thrift 负责进程间通信,gmock 则是 bmv2 单元测试的依赖。它们之间的版本约束非常硬:p4c 依赖 protobuf 3.2.0 和 thrift 0.9.2,bmv2 又依赖 gmock 1.7.0 做测试。你如果图省事用系统自带的 protobuf(比如 Ubuntu 仓库里的 3.6+),p4c 编译时就会因为 API 变动直接挂掉。

我一般会先把版本对照表列出来,装之前心里有数:

组件版本作用不锁版本的后果
protobuf3.2.0序列化库高版本 API 变更,p4c 编译失败
thrift0.9.2RPC 通信高版本头文件路径变化,链接报错
gmock1.7.0单元测试框架bmv2 测试编译不过
bmv2源码编译软件交换机依赖上述三者
p4c源码编译P4 编译器依赖 protobuf + thrift

提示:不要用apt install protobuf-compiler装系统包,版本对不上,后面会花更多时间卸载重装。

2.2 系统环境与基础依赖安装

我用的系统是 Ubuntu 16.04 或 18.04,这两个版本对老组件的兼容性最好。20.04 以上也能装,但 gmock-1.7.0 编译时会有额外的 C++ 标准问题,需要手动打补丁,新手不建议。先更新源并装一批基础工具:

# 更新包索引 sudo apt-get update # 安装编译工具链和基础依赖 sudo apt-get install -y \ git \ automake \ cmake \ libtool \ make \ g++ \ pkg-config \ libboost-dev \ libboost-thread-dev \ libboost-system-dev \ libboost-program-options-dev \ libboost-test-dev \ libgmp-dev \ libpcap-dev \ libssl-dev \ python3 \ python3-pip \ python2.7 \ python-pip

这里有几个点值得说。libboost-*-dev是 bmv2 编译必须的,缺一个就会在configure阶段报找不到 boost 库。libpcap-dev是 bmv2 抓包用的,不装的话 bmv2 能编译但跑不起来。Python 2.7 和 Python 3 都装上,是因为 thrift 0.9.2 的编译脚本里有些地方还在调 python2,而 p4c 的部分工具又用 python3,两个都留着最省事。

装完之后验证一下关键工具版本:

g++ --version # 确认 g++ 在 5.x 以上 cmake --version # 确认 cmake 在 3.x 以上 python3 --version # 确认 python3 可用

如果 g++ 版本低于 5,后面编译 protobuf 会报 C++11 特性不支持。Ubuntu 16.04 默认 g++ 是 5.4,够用;14.04 默认是 4.8,需要手动升级。

2.3 目录规划与源码下载

我习惯把所有源码放在~/p4-env下,统一管理,后面编译出问题也好找:

mkdir -p ~/p4-env cd ~/p4-env # 下载各组件源码(这里用 git clone,方便切版本) git clone https://github.com/google/googletest.git gmock-1.7.0 cd gmock-1.7.0 git checkout release-1.7.0 cd .. git clone https://github.com/protocolbuffers/protobuf.git protobuf-3.2.0 cd protobuf-3.2.0 git checkout v3.2.0 cd .. git clone https://github.com/apache/thrift.git thrift-0.9.2 cd thrift-0.9.2 git checkout 0.9.2 cd .. git clone https://github.com/p4lang/behavioral-model.git bmv2 git clone https://github.com/p4lang/p4c.git p4c

注意:如果网络拉取慢,可以换成对应的 release tarball 下载,但一定要确认版本号完全一致,不要用 master 分支。

3. 编译安装 protobuf 与 thrift:两个最容易翻车的依赖

3.1 protobuf-3.2.0 编译与安装

protobuf 是整个链条的底座,它装不好,p4c 和 bmv2 都别想编译过。3.2.0 这个版本比较老,编译时要注意先跑autogen.sh生成 configure 脚本:

cd ~/p4-env/protobuf-3.2.0 # 生成 configure 脚本 ./autogen.sh # 配置安装路径,统一装到 /usr/local ./configure --prefix=/usr/local # 编译,-j 后面跟 CPU 核心数,我一般用 4 make -j4 # 安装 sudo make install # 刷新动态库缓存 sudo ldconfig

--prefix=/usr/local是默认路径,装完后头文件在/usr/local/include/google/protobuf,库文件在/usr/local/lib。ldconfig这一步很多人会忘,不跑的话运行 bmv2 时会报error while loading shared libraries: libprotobuf.so.10,因为系统找不到刚装的库。

装完验证:

protoc --version # 应该输出 libprotoc 3.2.0

如果输出的是系统自带的版本(比如 2.6.1),说明/usr/local/bin不在 PATH 前面,需要手动调整:

export PATH=/usr/local/bin:$PATH

把这行加到~/.bashrc里,后面 thrift 和 bmv2 也依赖这个路径顺序。

3.2 thrift-0.9.2 编译与安装

thrift 0.9.2 的坑主要在编译选项上。它默认会编译很多语言绑定(Java、Ruby、PHP 等),我们只需要 C++ 和 Python,多余的绑定不仅拖慢编译,还可能因为缺少对应语言环境而报错。所以配置时要显式关掉:

cd ~/p4-env/thrift-0.9.2 # 生成 configure 脚本 ./bootstrap.sh # 只编译 C++ 和 Python 绑定,关掉其他语言 ./configure --prefix=/usr/local \ --without-java \ --without-ruby \ --without-php \ --without-perl \ --without-csharp \ --without-erlang \ --without-go \ --without-nodejs make -j4 sudo make install sudo ldconfig

--without-*这一串不是可选项,是必选项。我第一次装的时候没加,编译到 Java 绑定时直接报JAVA_HOME not set,白白浪费十分钟。关掉之后编译时间从十几分钟降到三四分钟。

装完验证:

thrift --version # 应该输出 Thrift version 0.9.2

如果报thrift: command not found,同样是 PATH 问题,确认/usr/local/bin在 PATH 里。

3.3 gmock-1.7.0 的编译与安装

gmock-1.7.0 是 bmv2 单元测试的依赖,虽然不装也能编译 bmv2 主体,但后面跑测试时会报缺 gmock。它的编译方式和其他组件不太一样,1.7.0 版本还没有 CMakeLists,需要用 makefile:

cd ~/p4-env/gmock-1.7.0 # 生成 Makefile cmake . # 编译 make # 安装头文件和库 sudo cp -r include/gtest /usr/local/include/ sudo cp -r include/gmock /usr/local/include/ sudo cp lib/libgtest.a /usr/local/lib/ sudo cp lib/libgmock.a /usr/local/lib/ sudo ldconfig

这里没有make install,因为 1.7.0 的 CMake 配置里没写 install 规则,手动拷贝是最稳的方式。拷贝完确认一下:

ls /usr/local/include/gtest ls /usr/local/lib/libgtest.a

两个都存在就说明装好了。

4. 编译 bmv2 与 p4c:把交换机和编译器装起来

4.1 behavioral-model 编译与安装

bmv2 是 P4 的软件交换机,编译它之前要确保 protobuf、thrift、gmock 都已经装好并且ldconfig跑过。bmv2 的源码里带了autogen.sh,流程比较标准:

cd ~/p4-env/bmv2 # 生成 configure 脚本 ./autogen.sh # 配置,开启 thrift 和 debug 支持 ./configure --prefix=/usr/local \ --with-thrift \ --enable-debugger make -j4 sudo make install sudo ldconfig

--with-thrift是必须的,bmv2 的运行时控制接口依赖 thrift。--enable-debugger是可选的,开了之后可以用p4dbg调试,学习阶段建议开。编译过程中如果报cannot find -lprotobuf,说明 protobuf 的库路径没被找到,检查/usr/local/lib下有没有libprotobuf.so,以及/etc/ld.so.conf.d/里有没有包含/usr/local/lib。

装完验证 bmv2 的核心可执行文件:

which simple_switch which simple_switch_CLI # 两个都应该输出 /usr/local/bin/simple_switch 和 /usr/local/bin/simple_switch_CLI

如果simple_switch存在但运行时报库找不到,再跑一次sudo ldconfig,然后ldd $(which simple_switch)看看有没有not found的依赖。

4.2 p4c 编译与安装

p4c 是 P4 编译器,它把.p4源码编译成 bmv2 能加载的 JSON 文件。p4c 的编译依赖比 bmv2 更多,除了 protobuf 和 thrift,还需要 boost 和 Python 的一些模块。先装 Python 依赖:

pip3 install --upgrade pip pip3 install ply pip2 install ply

ply是 p4c 前端解析 P4 语法用的,python2 和 python3 都装上是因为 p4c 的编译脚本在不同阶段会调不同版本的 Python。然后编译 p4c:

cd ~/p4-env/p4c # 创建构建目录,p4c 推荐 out-of-source 编译 mkdir -p build cd build # 用 cmake 配置 cmake .. \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DENABLE_BMV2=ON \ -DENABLE_EBPF=OFF \ -DENABLE_UBPF=OFF \ -DENABLE_DPDK=OFF \ -DENABLE_P4TEST=ON make -j4 sudo make install sudo ldconfig

这里几个-DENABLE_*参数值得解释。ENABLE_BMV2=ON是必须的,它让 p4c 生成 bmv2 后端。ENABLE_EBPF、ENABLE_UBPF、ENABLE_DPDK都关掉,因为我们只用 bmv2,开了反而会因为缺少对应内核头文件或库而报错。ENABLE_P4TEST=ON是开测试工具,后面验证时会用到。

装完验证:

p4c --version # 应该输出 p4c 的版本信息 p4c-bm2-ss --help # 应该输出 bmv2 后端的帮助信息

如果p4c-bm2-ss找不到,说明 bmv2 后端没编译进去,回头检查 cmake 配置里ENABLE_BMV2是不是 ON。

4.3 环境变量收尾配置

所有组件装完后,把路径统一写进~/.bashrc,避免每次开终端都要手动 export:

# 编辑 ~/.bashrc,在末尾追加 export PATH=/usr/local/bin:$PATH export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH export PYTHONPATH=/usr/local/lib/python3.5/site-packages:$PYTHONPATH

LD_LIBRARY_PATH这行是给运行时找动态库用的,虽然ldconfig已经配了系统级路径,但有些 Python 脚本调 bmv2 的 so 文件时还是依赖这个变量。PYTHONPATH里的版本号根据你的 Python 版本调整,Ubuntu 16.04 是 python3.5,18.04 是 python3.6。

改完执行:

source ~/.bashrc

5. 避坑与常见问题排查:那些让我重装三次的报错

5.1 protobuf 版本冲突导致 p4c 编译失败

现象:编译 p4c 时在链接阶段报undefined reference to google::protobuf::...,或者报libprotobuf.so.10: cannot open shared object file。

原因:系统里同时存在 apt 装的 protobuf 和手动编译的 protobuf,链接器优先找到了系统版本(通常是 2.6.1),而 p4c 需要 3.2.0 的符号。

解决:先确认protoc --version输出的是 3.2.0。如果不是,检查/usr/local/bin是否在 PATH 最前面。然后彻底清理系统 protobuf:

sudo apt-get remove --purge protobuf-compiler libprotobuf-dev sudo apt-get autoremove sudo ldconfig

重新source ~/.bashrc后再编译 p4c。

5.2 thrift 编译时 Python 模块缺失

现象:makethrift 时在py/目录下报Python.h: No such file or directory。

原因:缺少 Python 开发头文件,python3-dev或python-dev没装。

解决:

sudo apt-get install -y python3-dev python-dev

装完重新./configure再make。如果只缺 python3 的头文件,也可以只装python3-dev,但 thrift 0.9.2 的 configure 脚本会同时检查 python2 和 python3,两个都装最省事。

5.3 bmv2 运行时找不到 thrift 库

现象:simple_switch能编译出来,但一运行就报error while loading shared libraries: libthrift-0.9.2.so: cannot open shared object file。

原因:thrift 装到了/usr/local/lib,但系统动态库缓存没更新,或者LD_LIBRARY_PATH没包含这个路径。

解决:

sudo ldconfig echo $LD_LIBRARY_PATH # 确认输出里包含 /usr/local/lib

如果没有,把export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH加到~/.bashrc并source一下。

5.4 p4c 编译时 boost 版本不匹配

现象:cmake 配置 p4c 时报Could NOT find Boost (missing: boost_system),或者编译时报boost::filesystem相关错误。

原因:boost 开发包没装全,或者版本太老。p4c 需要 boost 1.58 以上。

解决:

sudo apt-get install -y libboost-all-dev

Ubuntu 16.04 的libboost-all-dev是 1.58,刚好够用。18.04 是 1.65,也没问题。如果系统源里的 boost 太老,就需要手动编译 boost,但这种情况很少见。

5.5 gmock 链接顺序导致测试编译失败

现象:编译 bmv2 的测试用例时报undefined reference to testing::...。

原因:gmock 的库文件链接顺序不对,-lgmock必须在-lgtest前面,否则符号解析不到。

解决:如果是手动改 Makefile,确认链接顺序是-lgmock -lgtest -lpthread。如果是 cmake 项目,检查target_link_libraries里的顺序。我一般直接改 bmv2 的Makefile.am,把 gmock 放在 gtest 前面。

6. 装完怎么验证:用一个最小 P4 程序跑通全链路

6.1 写一个最小 P4 程序

验证环境是否真正跑通,最直接的方式是编译一个 P4 程序并让 bmv2 加载。先写一个最简单的test.p4:

/* 最小 P4 程序:只做转发,不修改任何字段 */ #include <core.p4> #include <v1model.p4> header ethernet_t { bit<48> dstAddr; bit<48> srcAddr; bit<16> etherType; } struct metadata { } struct headers { ethernet_t ethernet; } 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 { /* 空 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); } } control MyComputeChecksum(inout headers hdr, inout metadata meta) { apply { } } V1Switch(MyParser(), MyIngress(), MyEgress(), MyComputeChecksum(), MyDeparser()) main;

这个程序虽然简单,但覆盖了 P4 程序的完整结构:parser、ingress、egress、deparser。能编译通过说明 p4c 的前端和后端都正常。

6.2 编译并加载到 bmv2

先编译成 bmv2 的 JSON 格式:

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

--p4v 1.6指定 P4 语言版本,--emit-externs让编译器输出外部声明信息,bmv2 加载时需要。如果这条命令成功,当前目录下会出现test.json,大小在几 KB 左右。

然后启动 bmv2 加载这个 JSON:

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

-i 0@veth0表示把 bmv2 的端口 0 绑定到系统的 veth0 接口。veth 接口需要提前创建:

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

如果simple_switch启动后没有报错,并且输出里显示Adding interface veth0 as port 0,说明 bmv2 已经成功加载了 P4 程序。

6.3 用 simple_switch_CLI 验证运行时

bmv2 启动后,另开一个终端,用simple_switch_CLI连接运行时接口:

simple_switch_CLI

进入 CLI 后,输入:

runtime_CLI> show_tables

如果输出里列出了 P4 程序中定义的表(我们这个最小程序没有表,所以输出为空),说明 thrift 通信正常。再输入:

runtime_CLI> show_ports

应该能看到端口 0 和端口 1 的信息。这两个命令能正常执行,说明 bmv2 的运行时接口、thrift 通信、P4 程序加载三个环节全部打通。

6.4 我每次装完都会跑的三条检查

装完 P4 环境后,我不会只看编译有没有报错,而是固定跑三条检查,确认整条链路真的能用:

检查项命令预期结果
编译器版本p4c --version输出 p4c 版本号
后端可用p4c-bm2-ss --help输出帮助信息,不报错
交换机可加载sudo simple_switch test.json启动无报错,端口绑定成功

这三条都过了,才说明环境是真的装好了,而不是“编译过了但跑不起来”。从那以后我每次装 P4 环境,都会在最后强制走一遍这个最小程序的编译和加载流程,因为编译通过和运行通过之间,往往还隔着动态库路径、thrift 版本、端口权限这几个坑。希望这套流程能帮你少走点弯路。

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

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

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

立即咨询