]> git.puffer.fish Git - matthieu/frr.git/commitdiff
rustbind: capture rust binary based daemon skeleton code work
authorChristian Hopps <chopps@labn.net>
Sun, 22 Sep 2024 04:46:48 +0000 (00:46 -0400)
committerChristian Hopps <chopps@labn.net>
Mon, 3 Mar 2025 11:42:07 +0000 (11:42 +0000)
Signed-off-by: Christian Hopps <chopps@labn.net>
lib/libfrr.h
rustbind/.cargo/config.toml [new file with mode: 0644]
rustbind/.gitignore [new file with mode: 0644]
rustbind/Cargo.toml.in [new file with mode: 0644]
rustbind/Makefile [new file with mode: 0644]
rustbind/README.org [new file with mode: 0644]
rustbind/build.rs.in [new file with mode: 0644]
rustbind/rustbin_init.c [new file with mode: 0644]
rustbind/rustbin_main.rs [new file with mode: 0644]
rustbind/subdir.am [new file with mode: 0644]
rustbind/wrapper.h.in [new file with mode: 0644]

index df537e2e3b43593c2d68634bb31033813beb8245..c08374811c9e8af468b7133343a691201e346c0e 100644 (file)
@@ -101,6 +101,7 @@ DECLARE_DLIST(log_args, struct log_arg, itm);
 #define PATH_VTY_PORT 2621
 #define PIM6D_VTY_PORT 2622
 #define MGMTD_VTY_PORT 2623
+#define RUSTBIND_VTY_PORT 2624
 /* Registry of daemons' port defaults */
 
 enum frr_cli_mode {
diff --git a/rustbind/.cargo/config.toml b/rustbind/.cargo/config.toml
new file mode 100644 (file)
index 0000000..0ac4faf
--- /dev/null
@@ -0,0 +1,2 @@
+[build]
+target-dir = "../build/rustd/target"
diff --git a/rustbind/.gitignore b/rustbind/.gitignore
new file mode 100644 (file)
index 0000000..5a44eef
--- /dev/null
@@ -0,0 +1 @@
+/Cargo.lock
diff --git a/rustbind/Cargo.toml.in b/rustbind/Cargo.toml.in
new file mode 100644 (file)
index 0000000..dfa4def
--- /dev/null
@@ -0,0 +1,19 @@
+[package]
+name = "rustbind"
+version = "0.1.0"
+edition = "2021"
+default-run = "rustbind"
+
+[[bin]]
+name = "rustbind"
+test = false
+bench = false
+path = "@srcdir@/rustbin_main.rs"
+
+[dependencies]
+tracing = "0.1.40"
+tracing-subscriber = { version = "0.3.18", features = ["env-filter"]}
+
+[build-dependencies]
+bindgen = "0.70.1"
+cc = "1.1.18"
diff --git a/rustbind/Makefile b/rustbind/Makefile
new file mode 100644 (file)
index 0000000..dce0d2d
--- /dev/null
@@ -0,0 +1,10 @@
+all: ALWAYS
+       @$(MAKE) -s -C .. rustbind/rustbind
+%: ALWAYS
+       @$(MAKE) -s -C .. rustbind/$@
+
+Makefile:
+       #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/rustbind/README.org b/rustbind/README.org
new file mode 100644 (file)
index 0000000..b41c0c6
--- /dev/null
@@ -0,0 +1,22 @@
+* Rust Binary Daemon Skeleton
+
+This is the skeleton for a native rust based FRR daemon. The daemon is built as
+a rust binary with bindgen access to FRR infra.
+
+** Development
+*** Editor/LSP support
+
+In order for LSP support to work you will likely need to do a couple things if
+you use a build directory.
+
+- Create 2 symlinks from the generated files in ~BUILDDIR/rustbind~ for
+   `build.rs` and `wrapper.h` e.g. if your build dir is in `frr/build`:
+
+  #+begin_src bash
+    cd frr/rustbind
+    ln -s ../build/rustbind/build.rs .
+    ln -s ../build/rustbind/wrapper.h .
+  #+end_src
+
+- Copy the created `Cargo.toml` from BUILDDIR and modify the path value to be
+  `rustbin_main.rs` (i.e., no path prefix).
diff --git a/rustbind/build.rs.in b/rustbind/build.rs.in
new file mode 100644 (file)
index 0000000..cf2222e
--- /dev/null
@@ -0,0 +1,95 @@
+extern crate bindgen;
+// extern crate gcc;
+
+use std::env;
+use std::path::PathBuf;
+
+use bindgen::callbacks::{MacroParsingBehavior, ParseCallbacks};
+use std::sync::{Arc, RwLock};
+
+use std::collections::HashSet;
+
+//
+// This complexity is need to ignore a #define in netdb.h that is shadowing an
+// anonymous enum in netinet/in.h
+//
+#[derive(Debug)]
+struct MacroCallback {
+    macros: Arc<RwLock<HashSet<String>>>,
+}
+
+impl ParseCallbacks for MacroCallback {
+    fn will_parse_macro(&self, name: &str) -> MacroParsingBehavior {
+        self.macros.write().unwrap().insert(name.into());
+
+        if name == "IPPORT_RESERVED" {
+            return MacroParsingBehavior::Ignore;
+        }
+
+        MacroParsingBehavior::Default
+    }
+}
+
+fn main() {
+    // Tell cargo to look for shared libraries in the specified directory
+    println!("cargo:rustc-link-search=@abs_top_builddir@/lib/.libs/libfrr.so");
+
+    // Tell cargo to tell rustc to link libfrr shared library.
+    println!("cargo:rustc-link-lib=frr");
+
+    // Tell cargo to invalidate the built crate whenever the wrapper changes
+    // println!("cargo:rerun-if-changed=@abs_srcdir@/wrapper.h");
+    println!("cargo:rerun-if-changed=wrapper.h");
+
+    let macros = Arc::new(RwLock::new(HashSet::new()));
+
+    // The bindgen::Builder is the main entry point
+    // to bindgen, and lets you build up options for
+    // the resulting bindings.
+    let bindings = bindgen::Builder::default()
+        .clang_args(&[
+            "-I@abs_top_builddir@", // need to get this dynamically
+            "-I@abs_top_srcdir@",
+            "-I@abs_top_srcdir@/lib",
+            "-DHAVE_CONFIG_H",
+            "-D_ATOMIC_WANT_TYPEDEFS",
+        ])
+        // The input header we would like to generate
+        // bindings for.
+        // .header("@abs_srcdir@/wrapper.h")
+        .header("wrapper.h")
+        // Tell cargo to invalidate the built crate whenever any of the
+        // included header files changed.
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        .parse_callbacks(Box::new(MacroCallback {
+            macros: macros.clone(),
+        }))
+        // Finish the builder and generate the bindings.
+        .blocklist_type("IPPORT_.*")
+        .blocklist_type("IPPORT_RESERVED")
+        // avoid creating bindings for things with u128 which doesn't yet have a
+        // stable FFI, for now.
+
+        .blocklist_function("lyd_eval_xpath4")
+        .blocklist_function("q[efg]cvt.*")
+        .blocklist_function("strto.*")
+        .blocklist_function("strfrom.*")
+
+        .generate()
+        // Unwrap the Result and panic on failure.
+        .expect("Unable to generate bindings");
+
+    // Write the bindings to the $OUT_DIR/bindings.rs file.
+    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+    bindings
+        .write_to_file(out_path.join("bindings.rs"))
+        .expect("Couldn't write bindings!");
+
+    cc::Build::new()
+        .include("@abs_top_builddir@")
+        .include("@abs_top_srcdir@")
+        .include("@abs_top_srcdir@/lib")
+        .define("HAVE_CONFIG_H", None)
+        .file("@abs_srcdir@/rustbin_init.c")
+        .compile("c_init_code");
+}
diff --git a/rustbind/rustbin_init.c b/rustbind/rustbin_init.c
new file mode 100644 (file)
index 0000000..fa1dc74
--- /dev/null
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * September 9 2024, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2024, LabN Consulting, L.L.C.
+ */
+
+#include <lib/libfrr.h>
+#include <lib/zebra.h>
+#include <lib/privs.h>
+#include <lib/version.h>
+
+zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN};
+
+struct zebra_privs_t rustbind_privs = {
+#if defined(FRR_USER)
+       .user = FRR_USER,
+#endif
+#if defined FRR_GROUP
+       .group = FRR_GROUP,
+#endif
+#ifdef VTY_GROUP
+       .vty_group = VTY_GROUP,
+#endif
+       .caps_p = _caps_p,
+       .cap_num_p = array_size(_caps_p),
+       .cap_num_i = 0
+};
+
+
+static const struct frr_yang_module_info *const rustbind_yang_modules[] = {};
+
+/* clang-format off */
+FRR_DAEMON_INFO(rustbind, RUST,
+               .vty_port = RUSTBIND_VTY_PORT,
+               .proghelp = "Implementation of the RUST daemon template.",
+
+               .privs = &rustbind_privs,
+
+               .yang_modules = rustbind_yang_modules,
+               .n_yang_modules = array_size(rustbind_yang_modules),
+
+               /* mgmtd will load the per-daemon config file now */
+               .flags = FRR_NO_SPLIT_CONFIG,
+       );
+/* clang-format on */
+
+extern struct frr_daemon_info *rust_get_daemon_info(void);
+
+struct frr_daemon_info *rust_get_daemon_info(void)
+{
+       return &rustbind_di;
+}
diff --git a/rustbind/rustbin_main.rs b/rustbind/rustbin_main.rs
new file mode 100644 (file)
index 0000000..acbe930
--- /dev/null
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// September 9 2024, Christian Hopps <chopps@labn.net>
+//
+// Copyright (C) 2024 LabN Consulting, L.L.C.
+//
+
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+
+// #[path = "rust_server.rs"]
+// pub mod rust;
+// Need to figure best way to do shared source files
+// #[path = "../lib/rust_msg.rs"]
+// pub mod msg;
+
+use tracing::debug;
+
+extern "C" {
+    // This is our C init function we use to handle the complex compiler tricks
+    // FRR uses for initializing the daemon info structure.
+    fn rust_get_daemon_info() -> *mut frr_daemon_info;
+}
+
+///
+/// Setup the trace logging.
+///
+fn setup_logging() {
+    /*
+     * Enable some logging
+     */
+    let subscriber = tracing_subscriber::FmtSubscriber::builder()
+        .with_max_level(tracing::Level::TRACE)
+        .finish();
+    tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
+}
+
+///
+/// Main Function :)
+///
+fn main() {
+    setup_logging();
+
+    // Initialize FRR.
+
+    // create a vector of zero terminated CLI arg strings
+    let args = std::env::args()
+        .map(|arg| CString::new(arg).unwrap())
+        .collect::<Vec<CString>>();
+
+    // convert the strings to raw pointers
+    let c_args = args
+        .iter()
+        .map(|arg| arg.as_ptr())
+        .collect::<Vec<*const c_char>>();
+
+    // Get frr_daemon_info from our C init module
+    let di = unsafe { rust_get_daemon_info() };
+
+    // Initialize FRR
+    unsafe { frr_preinit(di, c_args.len() as i32, c_args.as_ptr() as *mut *mut i8) }
+
+    debug!("daemon name is {:?}", unsafe {
+        CStr::from_ptr((*di).progname)
+    });
+
+    let master = unsafe { frr_init() };
+    debug!("master thread: {:?}", master);
+
+    // Run!
+    debug!("Running main FRR loop");
+    unsafe { frr_run(master) };
+}
diff --git a/rustbind/subdir.am b/rustbind/subdir.am
new file mode 100644 (file)
index 0000000..299645c
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# rustbind -- RUSTBIN Daemon
+#
+
+if RUSTBIND
+
+EXTRA_DIST += rustbind/Cargo.toml
+sbin_PROGRAMS += rustbind/rustbind
+rustbind_rustbind_SOURCES = rustbind/rustbin_main.rs
+
+if DEV_BUILD
+RUSTBIND_BUILD_FLAG =
+RUSTBIND_TARGET_DIR = debug
+else
+RUSTBIND_BUILD_FLAG = --release
+RUSTBIND_TARGET_DIR = release
+endif
+
+rustbind/Cargo.lock: rustbind/Cargo.toml
+       (cd rustbind && cargo update $(RUSTBIND_BUILD_FLAG))
+
+rustbind/rustbind: rustbind/target/$(RUSTBIND_TARGET_DIR)/rustbind$(EXEEXT)
+       @printf "  CP       rustbind/rustbind\n"
+       @cp $< $@
+
+rustbind/target/$(RUSTBIND_TARGET_DIR)/rustbind$(EXEEXT): $(rustbind_rustbind_SOURCES) rustbind/Cargo.toml
+       @printf "  CARGO    $@\n"
+       @(cd rustbind && cargo -q build $(RUSTBIND_BUILD_FLAG))
+
+clean-rustbind:
+       (cd rustbind && cargo clean)
+else
+clean-rustbind:
+endif
diff --git a/rustbind/wrapper.h.in b/rustbind/wrapper.h.in
new file mode 100644 (file)
index 0000000..a818311
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * September 9 2024, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (C) 2024 LabN Consulting, L.L.C.
+ */
+#include "config.h"
+/* #include "typesafe.h" */
+/* #include "frratomic.h" */
+/* #include "sigevent.h" */
+/* #include "privs.h" */
+
+/* #include "log.h" */
+/* #include "getopt.h" */
+/* #include "module.h" */
+/* #include "hook.h" */
+/* #include "northbound.h" */
+
+#include <lib/libfrr.h>
+#include <lib/zebra.h>
+#include <lib/mgmt_msg_native.h>