The NLnet Labs Blog

Sign in Subscribe Routing

Introducing Roto: A Compiled Scripting Language for Rust

Team NLnet Labs

2025年5月21日 • 阅读需时3分钟 Introducing Roto: A Compiled Scripting Language for Rust Photo by Joseph Barrientos / Unsplash 作者:Terts Diepraam

我们正在开发一种用于 Rust 的嵌入式脚本语言。这种语言名为 Roto,旨在成为一种简单、快速且可靠的 Rust 应用程序脚本语言。

对 Roto 的需求源于 Rotonda,我们用 Rust 编写的 BGP 引擎。 成熟的 BGP 应用程序通常具有某种过滤传入路由宣告的方式。 这些过滤器的复杂性通常超出配置语言的功能。 通过 Rotonda,我们希望让我们的用户能够轻松编写更复杂的过滤器。 因此,我们决定赋予他们完整脚本语言的能力。

我们对这种语言有一些严格的要求。 首先,我们需要这些过滤器快速。 其次,Rotonda 是关键基础设施,因此运行时崩溃是不可接受的。 这排除了动态类型语言,Rust 社区中有很多这样的语言。[1] 我们需要一种静态类型语言,它可以为我们提供更高的类型安全性和速度。 最后,我们希望这种语言易于上手;它应该感觉像是你习惯使用的脚本语言的静态类型版本。

Roto 满足了我们的这一需求。 简而言之,它是一种静态类型、JIT 编译、热重载的嵌入式脚本语言。 为了获得良好的性能,Roto 脚本在运行时使用 cranelift 编译器后端编译为机器代码。

下面是一个 Roto 脚本的小例子。 在这个脚本中,我们定义了一个 filtermap,它会产生 acceptreject 的结果。 在这种情况下,当 IP 地址在给定范围内时,我们 accept

filtermap within_range(range: AddrRange, ip: IpAddr) {
  if range.contains(ip) {
    accept ip
  } else {
    reject
  }
}

我们可以编写一个更传统的 function,它可以简单地 return 一个值,而不是 filtermapfiltermap 是 Roto 支持的一种构造,可以更轻松地编写过滤器。

那里的 Roto 代码可能看起来很简单,但有一个问题:AddrRange 不是内置类型。 相反,它由宿主应用程序(例如 Rotonda)添加到 Roto,使其可用于脚本中。[2] 同样,AddrRange 上的 contains 方法也由宿主应用程序提供。 运行上述脚本所需的完整代码如下所示。 此示例也可在 我们的 GitHub 存储库中找到。

use std::net::IpAddr;
use std::path::Path;
use roto::{roto_method, FileTree, Runtime, Val, Verdict};
#[derive(Clone)]
struct AddrRange {
  min: IpAddr,
  max: IpAddr,
}
fn run_script(path: &Path) {
  // Create a runtime
  let mut runtime = Runtime::new();
  
  // Register the AddrRange type into the runtime with a docstring
  runtime
    .register_clone_type::<AddrRange>("A range of IP addresses")
    .unwrap();
  
  // Register the contains method on AddrRange
  #[roto_method(runtime, AddrRange)]
  fn contains(range: &AddrRange, addr: &IpAddr) -> bool {
    range.min <= addr && addr <= range.max
  }
  
  // Compile the program
  let program =
    FileTree::read(path).compile(runtime).unwrap();
  // Extract the Roto filtermap, which is accessed as a function
  let function = program
    .get_function::<(), (Val<AddrRange>, IpAddr), Verdict<IpAddr, ()>>(
     "within_range"
    )
    .unwrap();
  
  // Run the filtermap
  let range = AddrRange {
    min: "10.10.10.10".parse().unwrap(),
    max: "10.10.10.12".parse().unwrap(),
  };
  let in_range = "10.10.10.11".parse().unwrap();
  println!("{:?}", function.call(&mut (), range, in_range)));
  let out_of_range = "10.10.11.10".parse().unwrap();
  println!("{:?}", function.call(&mut (), range, out_of_range));
}

请注意,当脚本加载时,脚本中的任何内容都不会自动运行,就像许多其他脚本语言中发生的那样。 宿主应用程序决定从脚本中提取哪些函数和 filtermap,以及何时运行它们。

Roto 与 Rust 紧密集成。 许多 Rust 类型[3]、方法和函数可以直接注册以在 Roto 中使用。 这些类型可以以极低的成本传递给 Roto; Roto 和 Rust 之间没有序列化。 对于 Rotonda,这意味着 Roto 可以对原始 BGP 消息进行操作,而无需昂贵的转换过程。

注册机制还确保 Roto 不仅限于 Rotonda,并且可以轻松地在该上下文之外使用。 它被设计为一种通用的脚本或插件语言。

我们在 Roto 的路线图上规划了许多功能,并将继续改进这种语言。 这也意味着该语言不应被认为是稳定的,但如果您尝试使用它,我们很乐意听到反馈。 如果您有兴趣,请查看 文档存储库示例

  1. 例如 Rhai, Rune, Mlua, Deno, PyO3, Dyon & Koto. ↩︎
  2. 请注意,类型的注册不受 Rust 的 孤儿规则 的阻碍,因为它除了 Clone 之外不需要任何特定的 traits。 这使得可以将来自外部库的类型公开给 Roto。 ↩︎
  3. 具体来说,是实现了 CloneCopy 的类型。 未实现这些 traits 的类型可以包装在 RcArc 中以传递给 Roto。 ↩︎

Sign up for more like this.

Enter your email Subscribe Prometheus Metrics in NSD 4.12.0

Prometheus Metrics in NSD 4.12.0 By Jannik Peters We finally implemented a Prometheus metrics endpoint, providing the statistics you know from nsd-control stats/stats_noreset in Prometheus format via HTTP. To enable the Prometheus metrics endpoint, specify the option metrics-enable: yes in the config's server section. You can then point your Prometheus exporter 2025年4月24日 阅读需时1分钟

Domain Foundations – The first of our five year vision

Domain Foundations – The first of our five year vision By Arya Khanna, Martin Hoffmann and Alex Band About a year ago, we unveiled our vision for the next five years of DNS at NLnet Labs. In this series of articles, we’d like to provide you with an update on our accomplishments last year and our plans for 2025 2025年1月7日 阅读需时7分钟

DNS-over-QUIC in Unbound

DNS-over-QUIC in Unbound By Wouter Wijngaards, with contributions from Yorgos Thessalonikefs DNS-over-QUIC (DoQ) uses the QUIC transport mechanism to encrypt queries and responses. The DoQ transport for DNS is defined in RFC 9250. With the recent release, Unbound can be configured to support DoQ clients downstream. This feature is not a standard component 2024年10月17日 阅读需时7分钟

The NLnet Labs Blog © 2025 Powered by Ghost