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