Skip to content

Commit

Permalink
Permit tcp syn-ack packets & config testing
Browse files Browse the repository at this point in the history
  • Loading branch information
containerscrew committed Dec 5, 2024
1 parent fdbbced commit e91bbcd
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 10 deletions.
38 changes: 33 additions & 5 deletions nflux-ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use aya_ebpf::{
programs::XdpContext,
};
use core::mem;
use network_types::ip::IpProto;
use network_types::{
eth::{EthHdr, EtherType},
ip::{IpProto, Ipv4Hdr},
ip::Ipv4Hdr,
tcp::TcpHdr,
udp::UdpHdr,
};
Expand Down Expand Up @@ -89,11 +90,36 @@ fn start_nflux(ctx: XdpContext) -> Result<u32, ()> {
let tcphdr: *const TcpHdr =
unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)? };
let dst_port = u16::from_be(unsafe { (*tcphdr).dest });

if rule.ports.contains(&dst_port) && rule.action == 1 {
log_new_connection(ctx, source_ip, dst_port, 6);
let syn = unsafe { (*tcphdr).syn() };
let ack = unsafe { (*tcphdr).ack() };

if syn == 1 && ack == 0 {
// SYN packet: Apply rules for incoming or outgoing connections
if rule.ports.contains(&dst_port) {
if rule.action == 1 {
log_new_connection(ctx, source_ip, dst_port, 6);
return Ok(xdp_action::XDP_PASS);
} else {
log_new_connection(ctx, source_ip, dst_port, 6);
return Ok(xdp_action::XDP_DROP);
}
}
} else if syn == 1 && ack == 1 {
// SYN-ACK packets: Allow for outgoing connection responses
return Ok(xdp_action::XDP_PASS);
} else if ack == 1 {
// ACK packets: Allow for established connections
return Ok(xdp_action::XDP_PASS);
}

// For other TCP packets, apply rules
if rule.ports.contains(&dst_port) {
if rule.action == 1 {
log_new_connection(ctx, source_ip, dst_port, 6);
return Ok(xdp_action::XDP_PASS);
}
}
log_new_connection(ctx, source_ip, dst_port, 6);
return Ok(xdp_action::XDP_DROP);
}
IpProto::Udp => {
Expand All @@ -105,7 +131,9 @@ fn start_nflux(ctx: XdpContext) -> Result<u32, ()> {
log_new_connection(ctx, source_ip, dst_port, 17);
return Ok(xdp_action::XDP_PASS);
}
return Ok(xdp_action::XDP_DROP);
// By the moment, allow every UDP packet
// Necessary to allow DNS UDP packets (internet browsing, for example)
return Ok(xdp_action::XDP_PASS);
}
IpProto::Icmp => {
if rule.action == 1 {
Expand Down
4 changes: 1 addition & 3 deletions nflux.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,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.174/32" = { priority = 1, action = "allow", ports = [22], protocol = "tcp", log = false, description = "Allow SSH for specific IP" }
"192.168.0.0/24" = { priority = 2, action = "deny", ports = [22], protocol = "tcp", log = false, description = "Deny SSH for entire subnet" }


"192.168.0.0/24" = { priority = 2, action = "deny", ports = [8081], protocol = "tcp", log = false, description = "Deny SSH for entire subnet" }

# todo: ipv6 support
# "2001:0db8:85a3:0000:0000:8a2e:0370:7334" = { action = "deny", ports = [80], protocol = "tcp" }
Expand Down
45 changes: 43 additions & 2 deletions nflux/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{Context, Result};
use serde::Deserialize;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs;

Expand Down Expand Up @@ -74,14 +74,29 @@ impl Nflux {

// A separate validation function to ensure correctness
pub fn validate(&self) -> Result<()> {
let mut priorities: HashSet<u32> = HashSet::new();

for (ip, rule) in &self.ip_rules {
// Ensure priority is greater than 0
if rule.priority == 0 {
anyhow::bail!("Priority must be greater than 0");
anyhow::bail!("Priority must be greater than 0 for rule: {}", ip);
}

// Ensure port numbers are within the valid range
if !rule.ports.iter().all(|&port| (1..=65535).contains(&port)) {
anyhow::bail!("Invalid port number in rule for IP: {}", ip);
}

// Check for duplicate priorities
if !priorities.insert(rule.priority) {
anyhow::bail!(
"Duplicate priority found: {} in rule for IP: {}",
rule.priority,
ip
);
}
}

Ok(())
}
}
Expand Down Expand Up @@ -148,6 +163,32 @@ mod tests {
.contains("Failed to read configuration file"));
}

#[test]
fn test_duplicate_priority() {
let config_content = r#"
[nflux]
interface_names = ["eth0", "wlan0"]
[logging]
log_level = "debug"
log_type = "json"
[ip_rules]
"192.168.0.1" = { priority = 1, action = "allow", ports = [22], protocol = "tcp", log = true, description = "SSH rule" }
"192.168.0.4" = { priority = 1, action = "allow", ports = [80], protocol = "tcp", log = true, description = "Nginx rule" }
"#;

let _temp_dir = setup_temp_config(config_content);

let config = Nflux::load_config();

// Check that the configuration loading fails due to duplicate priorities
assert!(
config.is_err(),
"Expected duplicate priorities to cause an error"
);
}

// #[test]
// fn test_load_invalid_config_format() {
// let invalid_config_content = "invalid: [toml";
Expand Down

0 comments on commit e91bbcd

Please sign in to comment.