From 9f29bbc338a13938da9529ac73f444a850d3a40c Mon Sep 17 00:00:00 2001 From: containerscrew Date: Sat, 7 Dec 2024 14:04:20 +0100 Subject: [PATCH] Working with TC egress implementation --- Cargo.lock | 101 ++++++++++++++++++++++++++++++++++----- nflux-ebpf/src/egress.rs | 36 ++++++++++++++ nflux-ebpf/src/lib.rs | 3 -- nflux-ebpf/src/main.rs | 33 ++----------- nflux.toml | 2 +- nflux/Cargo.toml | 1 + nflux/src/main.rs | 1 + nflux/src/utils.rs | 13 +++++ 8 files changed, 145 insertions(+), 45 deletions(-) create mode 100644 nflux-ebpf/src/egress.rs delete mode 100644 nflux-ebpf/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c693707..3e00333 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,7 +295,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -412,6 +412,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "dns-lookup" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.13.0" @@ -736,6 +748,7 @@ dependencies = [ "aya-log", "bytes", "cargo_metadata", + "dns-lookup", "env_logger", "libc", "log", @@ -845,7 +858,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1376,13 +1389,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1391,7 +1413,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1400,28 +1437,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1434,24 +1489,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/nflux-ebpf/src/egress.rs b/nflux-ebpf/src/egress.rs new file mode 100644 index 0000000..d8c4271 --- /dev/null +++ b/nflux-ebpf/src/egress.rs @@ -0,0 +1,36 @@ +use aya_ebpf::bindings::TC_ACT_PIPE; +use aya_ebpf::macros::map; +use aya_ebpf::maps::{LruHashMap, PerfEventArray}; +use aya_ebpf::programs::TcContext; +use network_types::eth::{EthHdr, EtherType}; +use network_types::ip::Ipv4Hdr; +use nflux_common::EgressEvent; + +#[map] +static ACTIVE_CONNECTIONS: LruHashMap = LruHashMap::with_max_entries(1024, 0); + +#[map] +static EGRESS_EVENT: PerfEventArray = PerfEventArray::new(0); + +pub fn try_tc_egress(ctx: TcContext) -> Result { + let ethhdr: EthHdr = ctx.load(0).map_err(|_| ())?; + match ethhdr.ether_type { + EtherType::Ipv4 => unsafe { + let ipv4hdr: Ipv4Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; + let destination = u32::from_be(ipv4hdr.dst_addr); + + // Check if this destination is already active + if ACTIVE_CONNECTIONS.get(&destination).is_none() { + // Log only new connections + let event = EgressEvent { dst_ip: destination }; + EGRESS_EVENT.output(&ctx, &event, 0); + + // Mark connection as active + ACTIVE_CONNECTIONS.insert(&destination, &1, 0).map_err(|_| ())?; + } + } + _ => return Ok(TC_ACT_PIPE), + } + + Ok(TC_ACT_PIPE) +} diff --git a/nflux-ebpf/src/lib.rs b/nflux-ebpf/src/lib.rs deleted file mode 100644 index 3ac3e59..0000000 --- a/nflux-ebpf/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![no_std] - -// This file exists to enable the library target. diff --git a/nflux-ebpf/src/main.rs b/nflux-ebpf/src/main.rs index fad1d8c..4ff9195 100644 --- a/nflux-ebpf/src/main.rs +++ b/nflux-ebpf/src/main.rs @@ -2,6 +2,8 @@ #![no_main] #![allow(nonstandard_style, dead_code)] +mod egress; + use aya_ebpf::maps::lpm_trie::Key; use aya_ebpf::maps::{Array, LpmTrie, LruHashMap}; use aya_ebpf::{ @@ -22,6 +24,7 @@ use network_types::{ udp::UdpHdr, }; use nflux_common::{ConnectionEvent, EgressEvent, IpRule, LpmKeyIpv4, LpmKeyIpv6}; +use crate::egress::try_tc_egress; #[map] static IPV4_RULES: LpmTrie = LpmTrie::with_max_entries(1024, 0); @@ -35,12 +38,6 @@ static CONNECTION_EVENTS: PerfEventArray = PerfEventArray::new( #[map] static ICMP_RULE: Array = Array::with_max_entries(1, 0); -#[map] -static EGRESS_EVENT: PerfEventArray = PerfEventArray::new(0); - -#[map] -static ACTIVE_CONNECTIONS: LruHashMap = LruHashMap::with_max_entries(1024, 0); - #[xdp] pub fn nflux(ctx: XdpContext) -> u32 { match start_nflux(ctx) { @@ -78,30 +75,6 @@ fn log_new_connection(ctx: XdpContext, src_addr: u32, dst_port: u16, protocol: u CONNECTION_EVENTS.output(&ctx, &event, 0); } -fn try_tc_egress(ctx: TcContext) -> Result { - let ethhdr: EthHdr = ctx.load(0).map_err(|_| ())?; - match ethhdr.ether_type { - EtherType::Ipv4 => unsafe { - let ipv4hdr: Ipv4Hdr = ctx.load(EthHdr::LEN).map_err(|_| ())?; - let destination = u32::from_be(ipv4hdr.dst_addr); - - // Check if this destination is already active - if ACTIVE_CONNECTIONS.get(&destination).is_none() { - // Log only new connections - let event = EgressEvent { dst_ip: destination }; - EGRESS_EVENT.output(&ctx, &event, 0); - - // Mark connection as active - ACTIVE_CONNECTIONS.insert(&destination, &1, 0).map_err(|_| ())?; - } - } - _ => return Ok(TC_ACT_PIPE), - } - - Ok(TC_ACT_PIPE) -} - - fn start_nflux(ctx: XdpContext) -> Result { let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? }; diff --git a/nflux.toml b/nflux.toml index 1f9ace9..76978bd 100644 --- a/nflux.toml +++ b/nflux.toml @@ -12,7 +12,7 @@ log_type = "text" # text or json. Defaults to text if not set [ip_rules] # The /32 CIDR block is used to represent a single IP address rather than a range -"192.168.0.0/24" = { priority = 1, action = "allow", ports = [22, 8000], protocol = "tcp", log = false, description = "Allow SSH for entire local net" } +"192.168.0.0/24" = { priority = 1, action = "allow", ports = [22, 8000, 80], protocol = "tcp", log = false, description = "Allow SSH for entire local net" } # curl -6 -v http://\[::ffff:192.168.0.26\]:80 "fe80::5bc2:662b:ac2f:7e8b/128" = { priority = 2, action = "allow", ports = [80], protocol = "tcp", log = false, description = "Deny HTTP for specific IPv6 address" } diff --git a/nflux/Cargo.toml b/nflux/Cargo.toml index 4fde1d8..c5cd8c1 100644 --- a/nflux/Cargo.toml +++ b/nflux/Cargo.toml @@ -20,6 +20,7 @@ toml = "0.8.19" prometheus = "0.13.4" axum = "0.7.7" bytes = "1.8.0" +dns-lookup = "2.0.4" [build-dependencies] cargo_metadata = { workspace = true } diff --git a/nflux/src/main.rs b/nflux/src/main.rs index d879a88..8b419e1 100644 --- a/nflux/src/main.rs +++ b/nflux/src/main.rs @@ -23,6 +23,7 @@ use tokio::task; use tracing::{error, info}; use utils::{is_root_user, wait_for_shutdown}; use crate::ebpf_mapping::populate_icmp_rule; +use crate::utils::lookup_address; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/nflux/src/utils.rs b/nflux/src/utils.rs index 2afc45d..d186df8 100644 --- a/nflux/src/utils.rs +++ b/nflux/src/utils.rs @@ -1,3 +1,5 @@ +use std::net::{IpAddr, Ipv4Addr}; +use dns_lookup::lookup_addr; use libc::getuid; use tokio::signal; use tracing::{info, warn}; @@ -14,3 +16,14 @@ pub async fn wait_for_shutdown() -> anyhow::Result<()> { warn!("Exiting..."); Ok(()) } + +pub fn lookup_address(ip: u32) -> String { + // Convert the u32 IP address to Ipv4Addr + let ip = Ipv4Addr::from(ip); + + // Convert to IpAddr for compatibility with lookup_addr + let ip = IpAddr::V4(ip); + + // Perform the reverse DNS lookup + lookup_addr(&ip).unwrap_or_else(|_| "Unknown host".to_string()) +}