Rust VPP API 绑定 | LFX 导师计划
大家好,这篇文章是关于 Rust VPP API Bindings 项目的,它是 LFX Mentorship 的一部分。我有这个绝佳的机会在Andrew Yourtchenko的指导下完成这个项目。所以让我们开始吧
什么是 VPP 🤔
FD.IO 是 Cisco 开发的在用户空间运行的开源数据平面。 FD.IO 的核心是 VPP,它代表矢量数据包处理器。因此,强调矢量,而不是标量数据包处理器,其中一次处理单个数据包,VPP 一次处理一个数据包矢量,从而获得高性能。
这就像用独轮车而不是勺子移动一堆弹珠
VPP 在 Cloud Native 和 Networking 领域有很多令人兴奋的应用,我将在这里留下链接供您探索🚀
项目印花布/VPP
VPP 案例研究
建筑🧐
这是我们项目的架构
[
](https://res.cloudinary.com/practicaldev/image/fetch/s--9cHn3xsA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/gg2px9agt7z37runma4s.png)
VPP-API-传输
此 crate 负责通过套接字或共享内存接口与正在运行的 VPP 进程建立连接。它还具有通过连接发送数据的各种功能。
VPP-API-编码
编码库包含负责与 VPP 交互的各种封装数据结构。该库主要确保数据结构的反序列化和序列化与 VPP 兼容。我们使用 serde+bincode 进行所有的序列化和反序列化为二进制。
VPP-API-驱动程序
驱动程序库由利用传输和编码成分的功能组成,这些功能允许更轻松地发送和接收消息。有 3 种类型的消息可以发送到 VPP。 (参考自 VPP 的开发者文档)
-
Request/Reply 客户端发送请求消息,服务器回复单个回复消息。约定是回复消息被命名为 method_name + Reply。例如:
SwInterfaceAddDelAddressReply -
Dump/Detail 客户端向服务器发送“批量”请求消息,服务器回复一组详细消息。这些消息可能是不同类型的。转储/详细信息调用必须包含在控制 ping 块中(否则客户端将不知道批量传输的结束)。方法名称必须以method + Dump结尾,回复消息应命名为method + Details。这里的例外是返回多种消息类型的方法(例如
SwInterfaceDump)。 Dump/Detail 方法通常用于获取批量信息,例如完整的 FIB 表或所有现有接口的详细信息。 -
Events 客户端可以注册从服务器获取异步通知。这对于获取接口状态更改等很有用。这是一种尚不支持的消息类型。
这是一个负责发送请求/回复消息的函数
pub fn send_recv_msg<'a, T: Serialize + Deserialize<'a>, TR: Serialize + DeserializeOwned>(
name: &str,
m: &T,
t: &mut dyn VppApiTransport,
reply_name: &str,
) -> TR {
let vl_msg_id = t.get_msg_index(name).unwrap();
let reply_vl_msg_id = t.get_msg_index(reply_name).unwrap();
let enc = get_encoder();
let mut v = enc.serialize(&vl_msg_id).unwrap();
let enc = get_encoder();
let msg = enc.serialize(&m).unwrap();
v.extend_from_slice(&msg);
println!("MSG[{} = 0x{:x}]: {:?}", name, vl_msg_id, &v);
t.write(&v);
loop {
let res = t.read_one_msg_id_and_msg();
// dbg!(&res);
if let Ok((msg_id, data)) = res {
println!("id: {} data: {:x?}", msg_id, &data);
if msg_id == reply_vl_msg_id {
let res = get_encoder()
.allow_trailing_bytes()
.deserialize::<TR>(&data)
.unwrap();
return res;
} else {
}
} else {
panic!("Result is an error: {:?}", &res);
}
}
}
进入全屏模式 退出全屏模式
VPP-API-Gen
这是 Rust VPP 最重要的部分,因为它摄取二进制 API 并将 Rust 绑定生成为一个单独的包,它只不过是 VPP-API。
[
](https://res.cloudinary.com/practicaldev/image/fetch/s--tGbSx-pv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/z5idj3qiavu7jbad711b.png)
VPP 提供二进制 API 方案以允许各种客户端代码对数据平面表进行编程。在撰写本文时,已有数百个二进制 API。消息在*.api个文件中定义。但是,我们使用的是在*.api.json*中定义的消息,它们是以 JSON 格式编写的自包含 api 文件。
VPP-API-宏
宏库由程序宏组成,这些宏使生成的 rust 绑定更符合人体工程学并提供复杂的抽象。 VPPMessage 宏是一个构建器宏,它允许您以不那么累人的方式初始化 VPP 消息,并且还实现消息名称和 crc 的派生。另一个宏VPPPunionIdent 为联合创建访问器方法,这使用户更容易初始化它,而无需填充整个联合。
这是使用构建器的示例
let create_host_interface: CliInbandReply = send_recv_msg(
&CliInband::get_message_name_and_crc(),
&CliInband::builder()
.client_index(t.get_client_index())
.context(0)
.cmd("create host-interface name vpp1out".try_into().unwrap())
.build()
.unwrap(),
&mut *t,
&CliInbandReply::get_message_name_and_crc(),
);
进入全屏模式 退出全屏模式
未来和测试
-
集成测试目前仅确保消息是否发送到 VPP 并接收回来,而不检查它是否是我们期望的回复,它已使用 GitHub Actions 添加到存储库的 CI 中。
-
如前所述,我们目前不支持事件消息,但希望将来开发它。
拉取请求🤓
这是我所有的拉取请求:
-
Linux 基金会指导 - Rust VPP 绑定
-
EnumFlags 生成
-
联合访问器方法和示例文件操作
-
文档更新
-
用 EnumFlag 更新
-
给接口添加IP地址
-
EnumFlags 和 FixedSizeArray
我学到的东西😎
这个项目是我迄今为止做过的最好的项目,我探索并实现了许多 Rust 特性
-
Rust 宏:我学习并使用 Rust 实现了过程宏,并创建了 VPP-api-macros,这被证明在使代码抽象方面非常有用。
-
Serde:我探索了 Serde,它是一个用于序列化和反序列化的板条箱,我能够为 EnumFlags 和 Unions 实现自定义序列化和反序列化。
-
VPP:我花了一段时间才开始了解VPP,但到上半场结束时,我能够掌握VPP是什么以及它的作用。
-
Golang:我学习 Go 是为了探索 GoVPP 绑定并了解他们在生成绑定以及可能在 Rust VPP 中使用其中一些绑定的理念。
-
特质:在指导之前,我仍在努力正确理解特质,但在项目过程中,我对自己对特质的理解变得更加自信
-
Rust 中的函数式编程:项目的部分内容是使用函数式编程的概念编写的,我不得不学习一些函数式编程,因为我主要使用 OOP 进行编程。
谢谢🥳
向 Rust Foundation Discord Server 致敬,感谢他们总是帮助我解决我的问题,无论他们多么愚蠢。Jon Gjengset为 Rust 的困难部分制作了大量视频,Linux 基金会为这个令人惊叹的程序提供了帮助,最后我的导师 Andrew 在项目的每一步指导我。
更多推荐


所有评论(0)