#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 {
--- /dev/null
+[build]
+target-dir = "../build/rustd/target"
--- /dev/null
+/Cargo.lock
--- /dev/null
+[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"
--- /dev/null
+all: ALWAYS
+ @$(MAKE) -s -C .. rustbind/rustbind
+%: ALWAYS
+ @$(MAKE) -s -C .. rustbind/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
--- /dev/null
+* 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).
--- /dev/null
+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");
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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) };
+}
--- /dev/null
+#
+# 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
--- /dev/null
+// 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>