大家好,这篇文章是关于 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 的开发者文档)

  1. Request/Reply 客户端发送请求消息,服务器回复单个回复消息。约定是回复消息被命名为 method_name + Reply。例如:SwInterfaceAddDelAddressReply

  2. Dump/Detail 客户端向服务器发送“批量”请求消息,服务器回复一组详细消息。这些消息可能是不同类型的。转储/详细信息调用必须包含在控制 ping 块中(否则客户端将不知道批量传输的结束)。方法名称必须以method + Dump结尾,回复消息应命名为method + Details。这里的例外是返回多种消息类型的方法(例如SwInterfaceDump)。 Dump/Detail 方法通常用于获取批量信息,例如完整的 FIB 表或所有现有接口的详细信息。

  3. 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 在项目的每一步指导我。

Logo

ModelScope旨在打造下一代开源的模型即服务共享平台,为泛AI开发者提供灵活、易用、低成本的一站式模型服务产品,让模型应用更简单!

更多推荐