feat: added basic rust wrapper around libnl

This commit is contained in:
Andrew Rioux 2023-05-01 01:50:27 -04:00
parent 2fc1916273
commit da9aa2178c
13 changed files with 406 additions and 8 deletions

View File

@ -15,7 +15,7 @@
FROM rust:1-alpine 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 && \ flex bison linux-headers openssl-dev apache2-utils docker-compose && \
mkdir /etc/docker && \ mkdir /etc/docker && \
echo '{ "storage-driver": "vfs" }' > /etc/docker/daemon.json echo '{ "storage-driver": "vfs" }' > /etc/docker/daemon.json

View File

@ -14,7 +14,8 @@
"rust-lang.rust-analyzer", "rust-lang.rust-analyzer",
"ymotongpoo.licenser", "ymotongpoo.licenser",
"ms-azuretools.vscode-docker", "ms-azuretools.vscode-docker",
"bungcip.better-toml" "bungcip.better-toml",
"vadimcn.vscode-lldb"
] ]
} }
}, },

100
.vscode/launch.json vendored Normal file
View File

@ -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}"
},
]
}

1
Cargo.lock generated
View File

@ -373,6 +373,7 @@ name = "nl-sys"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"autotools", "autotools",
"cc",
"libc", "libc",
] ]

BIN
core Normal file

Binary file not shown.

View File

@ -9,4 +9,5 @@ edition = "2021"
libc = "0.2.142" libc = "0.2.142"
[build-dependencies] [build-dependencies]
autotools = "0.2" autotools = "0.2"
cc = "1.0"

View File

@ -14,6 +14,10 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
fn main() { fn main() {
cc::Build::new()
.file("src/bridge.c")
.compile("bridge");
let dst = autotools::Config::new("libnl") let dst = autotools::Config::new("libnl")
.reconf("-vi") .reconf("-vi")
.build(); .build();

22
nl-sys/src/bridge.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <linux/netlink.h>
int netlink_route() {
return NETLINK_ROUTE;
}

48
nl-sys/src/error.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<T> = std::result::Result<T, Error>;

View File

@ -13,12 +13,24 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
enum nl_sock {} mod nl_ffi;
mod netlink;
mod route;
mod error;
extern { // from bridge.c
fn nl_socket_alloc() -> nl_sock; extern "C" {
pub(crate) fn netlink_route() -> libc::c_int;
} }
fn main() { fn main() -> error::Result<()> {
unsafe { nl_socket_alloc(); } let sock = netlink::Socket::new()?;
let links = sock.get_links()?;
for link in links.iter() {
println!("Link: {}", link.name());
}
Ok(())
} }

115
nl-sys/src/netlink.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<Self> {
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<Cache<crate::route::Link>> {
unsafe {
let mut link_cache = ptr::null_mut::<nl_cache>();
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<T>
where
T: From<*mut nl_object>
{
cache: *mut nl_cache,
dt: PhantomData<T>
}
impl<T: From<*mut nl_object>> Cache<T> {
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<T: From<*mut nl_object>> Drop for Cache<T> {
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<T: From<*mut nl_object>> Iterator for CacheIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
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<usize>) {
(self.cache_size, Some(self.cache_size))
}
}

54
nl-sys/src/nl_ffi.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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;
}

40
nl-sys/src/route.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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 _
}
}
}