Skip to content

Commit

Permalink
Working with TC egress implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
containerscrew committed Dec 7, 2024
1 parent 28a0234 commit 9f29bbc
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 45 deletions.
101 changes: 90 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions nflux-ebpf/src/egress.rs
Original file line number Diff line number Diff line change
@@ -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<u32, u32> = LruHashMap::with_max_entries(1024, 0);

#[map]
static EGRESS_EVENT: PerfEventArray<EgressEvent> = PerfEventArray::new(0);

pub fn try_tc_egress(ctx: TcContext) -> Result<i32, ()> {
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)
}
3 changes: 0 additions & 3 deletions nflux-ebpf/src/lib.rs

This file was deleted.

33 changes: 3 additions & 30 deletions nflux-ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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<LpmKeyIpv4, IpRule> = LpmTrie::with_max_entries(1024, 0);
Expand All @@ -35,12 +38,6 @@ static CONNECTION_EVENTS: PerfEventArray<ConnectionEvent> = PerfEventArray::new(
#[map]
static ICMP_RULE: Array<u32> = Array::with_max_entries(1, 0);

#[map]
static EGRESS_EVENT: PerfEventArray<EgressEvent> = PerfEventArray::new(0);

#[map]
static ACTIVE_CONNECTIONS: LruHashMap<u32, u32> = LruHashMap::with_max_entries(1024, 0);

#[xdp]
pub fn nflux(ctx: XdpContext) -> u32 {
match start_nflux(ctx) {
Expand Down Expand Up @@ -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<i32, ()> {
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<u32, ()> {
let ethhdr: *const EthHdr = unsafe { ptr_at(&ctx, 0)? };

Expand Down
2 changes: 1 addition & 1 deletion nflux.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
1 change: 1 addition & 0 deletions nflux/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
1 change: 1 addition & 0 deletions nflux/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand Down
13 changes: 13 additions & 0 deletions nflux/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::net::{IpAddr, Ipv4Addr};
use dns_lookup::lookup_addr;
use libc::getuid;
use tokio::signal;
use tracing::{info, warn};
Expand All @@ -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())
}

0 comments on commit 9f29bbc

Please sign in to comment.