diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 2c12090..54d52a9 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -15,7 +15,7 @@
FROM rust:1-alpine
-RUN apk add bash docker git cmake make automake musl-dev autoconf libtool \
+RUN apk add bash docker git cmake make automake musl-dev autoconf libtool valgrind \
flex bison linux-headers openssl-dev apache2-utils docker-compose && \
mkdir /etc/docker && \
echo '{ "storage-driver": "vfs" }' > /etc/docker/daemon.json
\ No newline at end of file
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index feff78d..63e9818 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -14,7 +14,8 @@
"rust-lang.rust-analyzer",
"ymotongpoo.licenser",
"ms-azuretools.vscode-docker",
- "bungcip.better-toml"
+ "bungcip.better-toml",
+ "vadimcn.vscode-lldb"
]
}
},
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..543c567
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,100 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug unit tests in library 'pcap-sys'",
+ "cargo": {
+ "args": [
+ "test",
+ "--no-run",
+ "--lib",
+ "--package=pcap-sys"
+ ],
+ "filter": {
+ "name": "pcap-sys",
+ "kind": "lib"
+ }
+ },
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ },
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug executable 'nl-sys'",
+ "cargo": {
+ "args": [
+ "build",
+ "--bin=nl-sys",
+ "--package=nl-sys"
+ ],
+ "filter": {
+ "name": "nl-sys",
+ "kind": "bin"
+ }
+ },
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ },
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug unit tests in executable 'nl-sys'",
+ "cargo": {
+ "args": [
+ "test",
+ "--no-run",
+ "--bin=nl-sys",
+ "--package=nl-sys"
+ ],
+ "filter": {
+ "name": "nl-sys",
+ "kind": "bin"
+ }
+ },
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ },
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug executable 'ex-bind-shell-backdoor'",
+ "cargo": {
+ "args": [
+ "build",
+ "--bin=ex-bind-shell-backdoor",
+ "--package=ex-bind-shell-backdoor"
+ ],
+ "filter": {
+ "name": "ex-bind-shell-backdoor",
+ "kind": "bin"
+ }
+ },
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ },
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug executable 'ex-bind-shell-client'",
+ "cargo": {
+ "args": [
+ "build",
+ "--bin=ex-bind-shell-client",
+ "--package=ex-bind-shell-client"
+ ],
+ "filter": {
+ "name": "ex-bind-shell-client",
+ "kind": "bin"
+ }
+ },
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ },
+ ]
+}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 5abce7f..9293020 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -373,6 +373,7 @@ name = "nl-sys"
version = "0.1.0"
dependencies = [
"autotools",
+ "cc",
"libc",
]
diff --git a/core b/core
new file mode 100644
index 0000000..ba7a4fe
Binary files /dev/null and b/core differ
diff --git a/nl-sys/Cargo.toml b/nl-sys/Cargo.toml
index 42fb58c..f48357c 100644
--- a/nl-sys/Cargo.toml
+++ b/nl-sys/Cargo.toml
@@ -9,4 +9,5 @@ edition = "2021"
libc = "0.2.142"
[build-dependencies]
-autotools = "0.2"
\ No newline at end of file
+autotools = "0.2"
+cc = "1.0"
\ No newline at end of file
diff --git a/nl-sys/build.rs b/nl-sys/build.rs
index 67d4255..374ace9 100644
--- a/nl-sys/build.rs
+++ b/nl-sys/build.rs
@@ -14,6 +14,10 @@
// along with this program. If not, see .
fn main() {
+ cc::Build::new()
+ .file("src/bridge.c")
+ .compile("bridge");
+
let dst = autotools::Config::new("libnl")
.reconf("-vi")
.build();
diff --git a/nl-sys/src/bridge.c b/nl-sys/src/bridge.c
new file mode 100644
index 0000000..c03b2f5
--- /dev/null
+++ b/nl-sys/src/bridge.c
@@ -0,0 +1,22 @@
+/**
+ * Copyright (C) 2023 Andrew Rioux
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+
+int netlink_route() {
+ return NETLINK_ROUTE;
+}
\ No newline at end of file
diff --git a/nl-sys/src/error.rs b/nl-sys/src/error.rs
new file mode 100644
index 0000000..c0ede71
--- /dev/null
+++ b/nl-sys/src/error.rs
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 Andrew Rioux
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use std::{fmt::Display, ffi::CStr};
+
+use libc::c_int;
+
+use crate::nl_ffi::nl_geterror;
+
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct Error {
+ error_code: c_int,
+}
+
+impl Error {
+ pub(crate) fn new(error_code: c_int) -> Self {
+ Error { error_code }
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let error_msg_utf8 = unsafe {
+ let error_msg = nl_geterror(self.error_code);
+ let error_msg_ptr = CStr::from_ptr(error_msg);
+ std::str::from_utf8(error_msg_ptr.to_bytes()).unwrap()
+ };
+
+ write!(f, "nternal libnl error: {error_msg_utf8}")
+ }
+}
+
+impl std::error::Error for Error {}
+
+pub type Result = std::result::Result;
\ No newline at end of file
diff --git a/nl-sys/src/main.rs b/nl-sys/src/main.rs
index 60594ec..291e85b 100644
--- a/nl-sys/src/main.rs
+++ b/nl-sys/src/main.rs
@@ -13,12 +13,24 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-enum nl_sock {}
+mod nl_ffi;
+mod netlink;
+mod route;
+mod error;
-extern {
- fn nl_socket_alloc() -> nl_sock;
+// from bridge.c
+extern "C" {
+ pub(crate) fn netlink_route() -> libc::c_int;
}
-fn main() {
- unsafe { nl_socket_alloc(); }
+fn main() -> error::Result<()> {
+ let sock = netlink::Socket::new()?;
+
+ let links = sock.get_links()?;
+
+ for link in links.iter() {
+ println!("Link: {}", link.name());
+ }
+
+ Ok(())
}
\ No newline at end of file
diff --git a/nl-sys/src/netlink.rs b/nl-sys/src/netlink.rs
new file mode 100644
index 0000000..7291edd
--- /dev/null
+++ b/nl-sys/src/netlink.rs
@@ -0,0 +1,115 @@
+// Copyright (C) 2023 Andrew Rioux
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use std::{ptr, marker::PhantomData};
+
+use libc::AF_UNSPEC;
+
+use crate::{nl_ffi::*, error};
+
+pub struct Socket {
+ sock: *mut nl_sock
+}
+
+impl Socket {
+ pub fn new() -> error::Result {
+ unsafe {
+ let sock = Socket { sock: nl_socket_alloc() };
+
+ let ret = nl_connect(sock.sock, crate::netlink_route());
+ if ret < 0 {
+ return Err(error::Error::new(ret));
+ }
+
+ Ok(sock)
+ }
+ }
+
+ pub fn get_links(&self) -> error::Result> {
+ unsafe {
+ let mut link_cache = ptr::null_mut::();
+
+ let ret = rtnl_link_alloc_cache(self.sock, AF_UNSPEC, &mut link_cache as *mut _);
+
+ if ret < 0 {
+ return Err(error::Error::new(ret));
+ }
+
+ Ok(Cache {
+ cache: link_cache,
+ dt: PhantomData
+ })
+ }
+ }
+}
+
+pub struct Cache
+where
+ T: From<*mut nl_object>
+{
+ cache: *mut nl_cache,
+ dt: PhantomData
+}
+
+impl> Cache {
+ pub fn iter(&self) -> CacheIter<'_, T> {
+ let cache_size = unsafe {
+ nl_cache_nitems(self.cache)
+ } as usize;
+
+ CacheIter {
+ obj: unsafe { nl_cache_get_first(self.cache) },
+ cache_size,
+ index: 0,
+ item_type: PhantomData {}
+ }
+ }
+}
+
+impl> Drop for Cache {
+ fn drop(&mut self) {
+ unsafe {
+ nl_cache_put(self.cache);
+ }
+ }
+}
+
+pub struct CacheIter<'a, T> {
+ obj: *mut nl_object,
+ cache_size: usize,
+ index: usize,
+ item_type: PhantomData<&'a T>,
+}
+
+impl> Iterator for CacheIter<'_, T> {
+ type Item = T;
+
+ fn next(&mut self) -> Option {
+ if self.index >= self.cache_size || self.obj.is_null() {
+ return None;
+ }
+
+ self.index += 1;
+
+ let obj = self.obj;
+ self.obj = unsafe { nl_cache_get_next(obj) };
+
+ Some(T::from(obj))
+ }
+
+ fn size_hint(&self) -> (usize, Option) {
+ (self.cache_size, Some(self.cache_size))
+ }
+}
diff --git a/nl-sys/src/nl_ffi.rs b/nl-sys/src/nl_ffi.rs
new file mode 100644
index 0000000..e9df0a9
--- /dev/null
+++ b/nl-sys/src/nl_ffi.rs
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 Andrew Rioux
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use libc::{c_int, c_void, c_char};
+
+macro_rules! nl_obj {
+ ($name:ident) => {
+ #[repr(C)]
+ #[allow(non_camel_case_types)]
+ pub struct $name {
+ _data: [u8; 0],
+ _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
+ }
+ }
+}
+
+nl_obj!(nl_sock);
+nl_obj!(nl_cache);
+nl_obj!(rtnl_link);
+nl_obj!(nl_addr);
+nl_obj!(nl_object);
+
+// from libnl and libnl-route
+extern "C" {
+ pub fn nl_socket_alloc() -> *mut nl_sock;
+ pub fn nl_socket_free(sock: *mut nl_sock);
+ pub fn nl_socket_get_local_port(sock: *const nl_sock) -> u32;
+ pub fn nl_connect(sock: *mut nl_sock, protocol: c_int) -> c_int;
+
+ pub fn nl_geterror(error: c_int) -> *const c_char;
+
+ pub fn nl_cache_foreach(cache: *mut nl_cache, cb: extern "C" fn(*mut nl_object, *mut c_void), arg: *mut c_void) -> c_void;
+ pub fn nl_cache_put(cache: *mut nl_cache) -> c_void;
+ pub fn nl_cache_nitems(cache: *mut nl_cache) -> c_int;
+ pub fn nl_cache_get_first(cache: *mut nl_cache) -> *mut nl_object;
+ pub fn nl_cache_get_next(obj: *mut nl_object) -> *mut nl_object;
+
+ pub fn rtnl_link_alloc_cache(sock: *mut nl_sock, family: c_int, result: *mut *mut nl_cache) -> c_int;
+ pub fn rtnl_link_get_by_name(cache: *mut nl_cache, name: *const c_char) -> *mut rtnl_link;
+ pub fn rtnl_link_get_addr(link: *mut rtnl_link) -> *mut nl_addr;
+ pub fn rtnl_link_get_name(link: *mut rtnl_link) -> *const c_char;
+}
diff --git a/nl-sys/src/route.rs b/nl-sys/src/route.rs
new file mode 100644
index 0000000..c20d5bf
--- /dev/null
+++ b/nl-sys/src/route.rs
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 Andrew Rioux
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use std::ffi::CStr;
+
+use super::nl_ffi::*;
+
+pub struct Link {
+ link: *mut rtnl_link
+}
+
+impl Link {
+ pub fn name(&self) -> String {
+ unsafe {
+ let name = rtnl_link_get_name(self.link);
+ let name_rs = CStr::from_ptr(name);
+ std::str::from_utf8(name_rs.to_bytes()).unwrap().to_owned()
+ }
+ }
+}
+
+impl From<*mut nl_object> for Link {
+ fn from(value: *mut nl_object) -> Self {
+ Self {
+ link: value as *mut _
+ }
+ }
+}
\ No newline at end of file