Skip to content

Commit

Permalink
Implement icmp enable/disable
Browse files Browse the repository at this point in the history
  • Loading branch information
containerscrew committed Dec 5, 2024
1 parent e91bbcd commit 14c39c3
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 40 deletions.
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ repos:
hooks:
- id: mtoc
args: [ "-e", ".target/" ]
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
args: ['--verbose', '--']
# - repo: https://github.com/doublify/pre-commit-rust
# rev: v1.0
# hooks:
# - id: fmt
# args: ['--verbose', '--']
# - id: cargo-check
# - id: clippy
# args: ["--", "-D", "warnings", "-D", "unused-imports"]
Expand Down
16 changes: 12 additions & 4 deletions nflux-ebpf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![allow(nonstandard_style, dead_code)]

use aya_ebpf::maps::lpm_trie::Key;
use aya_ebpf::maps::LpmTrie;
use aya_ebpf::maps::{Array, LpmTrie};
use aya_ebpf::{
bindings::xdp_action,
macros::{map, xdp},
Expand Down Expand Up @@ -35,6 +35,9 @@ static IPV6_RULES: LpmTrie<LpmKeyIpv6, IpRule> = LpmTrie::with_max_entries(1024,
#[map]
static CONNECTION_EVENTS: PerfEventArray<ConnectionEvent> = PerfEventArray::new(0);

#[map]
static ICMP_RULE: Array<u32> = Array::with_max_entries(1, 0);

#[xdp]
pub fn nflux(ctx: XdpContext) -> u32 {
match start_nflux(ctx) {
Expand Down Expand Up @@ -136,10 +139,15 @@ fn start_nflux(ctx: XdpContext) -> Result<u32, ()> {
return Ok(xdp_action::XDP_PASS);
}
IpProto::Icmp => {
if rule.action == 1 {
log_new_connection(ctx, source_ip, 0, 1);
return Ok(xdp_action::XDP_PASS);
// Read from EBPF map
if let Some(&icmp_ping) = ICMP_RULE.get(0) {
if icmp_ping == 1 {
// Allow ICMP packets if enabled
log_new_connection(ctx, source_ip, 0, 1);
return Ok(xdp_action::XDP_PASS);
}
}
// Block ICMP packets by default
return Ok(xdp_action::XDP_DROP);
}
_ => return Ok(xdp_action::XDP_DROP),
Expand Down
9 changes: 3 additions & 6 deletions nflux.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[nflux]
# Global configuration for nflux
interface_names = ["wlp2s0", "eth0"]
# Control ICMP ping packets for each IP I think is not necessary by the moment
# Lets decide if ICMP packets will be allowed or denied globally
icmp_ping = "false"

[logging]
log_level = "info" # trace, debug, info, warn, or error. Defaults to info if not set
Expand All @@ -15,12 +18,6 @@ log_type = "text" # text or json. Defaults to text if not set
# todo: ipv6 support
# "2001:0db8:85a3:0000:0000:8a2e:0370:7334" = { action = "deny", ports = [80], protocol = "tcp" }

# [icmp_rules]
# # Rules for ICMP traffic
# "192.168.0.1/24" = { action = "deny", protocol = "icmp" }
# "192.168.0.88/24" = { action = "allow", protocol = "icmp" }
# "192.168.0.22/24" = { action = "deny", protocol = "icmp" }

# [mac_rules]
# # Rules for MAC address filtering
# "00:0a:95:9d:68:16" = { action = "allow" }
Expand Down
42 changes: 21 additions & 21 deletions nflux/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,27 @@ pub enum Action {
Allow,
}

/// Enum for `protocol`
// Enum for `protocol`
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Protocol {
Tcp,
Udp,
Icmp,
}

// Enum for `is enabled`
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum IsEnabled {
True,
False,
}

// General firewall configuration
#[derive(Debug, Deserialize)]
pub struct NfluxConfig {
pub interface_names: Vec<String>,
pub icmp_ping: IsEnabled,
}

// Logging config
Expand All @@ -34,10 +42,10 @@ pub struct LoggingConfig {
pub log_type: String,
}

/// Generic rule for both IPv4 and IPv6
// Generic rule for both IPv4 and IPv6
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
pub struct Rules {
pub struct IpRules {
pub priority: u32,
pub action: Action,
pub ports: Vec<u16>,
Expand All @@ -52,7 +60,7 @@ pub struct Rules {
pub struct Nflux {
pub nflux: NfluxConfig,
pub logging: LoggingConfig,
pub ip_rules: HashMap<String, Rules>,
pub ip_rules: HashMap<String, IpRules>,
}

impl Nflux {
Expand Down Expand Up @@ -122,11 +130,13 @@ mod tests {
let config_content = r#"
[nflux]
interface_names = ["eth0", "wlan0"]
icmp_ping = "true"
[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" }
"#;
Expand All @@ -137,6 +147,7 @@ mod tests {

// Assertions
assert_eq!(config.nflux.interface_names, vec!["eth0", "wlan0"]);
assert_eq!(config.nflux.icmp_ping, IsEnabled::True);
assert_eq!(config.logging.log_level, "debug");
assert_eq!(config.logging.log_type, "json");

Expand Down Expand Up @@ -168,11 +179,16 @@ mod tests {
let config_content = r#"
[nflux]
interface_names = ["eth0", "wlan0"]
icmp_ping = "true"
[logging]
log_level = "debug"
log_type = "json"
[icmp_rules]
# Rules for ICMP traffic
"192.168.0.0/24" = { action = "deny", protocol = "icmp" }
[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" }
Expand All @@ -188,20 +204,4 @@ mod tests {
"Expected duplicate priorities to cause an error"
);
}

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

// setup_temp_config(invalid_config_content);

// let result = Nflux::load_config();

// // Assert that loading fails due to parse error
// assert!(result.is_err());
// assert!(result
// .unwrap_err()
// .to_string()
// .contains("Failed to parse configuration file"));
// }
}
21 changes: 21 additions & 0 deletions nflux/src/ebpf_mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use anyhow::Context;
use aya::Ebpf;
use aya::maps::{Array};
use crate::config::IsEnabled;

pub fn populate_icmp_rule(bpf: &mut Ebpf, icmp_ping: IsEnabled) -> anyhow::Result<()> {
let mut settings_map = Array::<_, u32>::try_from(
bpf.map_mut("ICMP_RULE").context("Failed to find GLOBAL_SETTINGS map")?,
)?;

let value = match icmp_ping {
IsEnabled::True => 1,
IsEnabled::False => 0,
};

settings_map
.set(0, value, 0)
.context("Failed to set ICMP_MAP")?;

Ok(())
}
10 changes: 6 additions & 4 deletions nflux/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod config;
mod core;
mod logger;
mod utils;
mod ebpf_mapping;

use anyhow::Context;
use aya::maps::lpm_trie::Key;
Expand All @@ -11,7 +12,7 @@ use aya::programs::{Xdp, XdpFlags};
use aya::util::online_cpus;
use aya::{include_bytes_aligned, Ebpf};
use bytes::BytesMut;
use config::{Action, Nflux, Protocol, Rules};
use config::{Action, Nflux, Protocol, IpRules};
use core::set_mem_limit;
use logger::setup_logger;
use nflux_common::{convert_protocol, ConnectionEvent, IpRule, LpmKeyIpv4};
Expand All @@ -21,6 +22,7 @@ use std::ptr;
use tokio::task;
use tracing::{error, info};
use utils::{is_root_user, wait_for_shutdown};
use crate::ebpf_mapping::populate_icmp_rule;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
Expand All @@ -44,6 +46,7 @@ async fn main() -> anyhow::Result<()> {

// Populate eBPF maps with configuration data
populate_ipv4_rules(&mut bpf, &config.ip_rules)?;
populate_icmp_rule(&mut bpf, config.nflux.icmp_ping)?;
// populate_ipv6_rules(&mut bpf, &config.ip_rules)?;

// Attach XDP program
Expand Down Expand Up @@ -120,7 +123,7 @@ fn parse_connection_event(buf: &BytesMut) -> anyhow::Result<ConnectionEvent> {
}
}

fn populate_ipv4_rules(bpf: &mut Ebpf, ip_rules: &HashMap<String, Rules>) -> anyhow::Result<()> {
fn populate_ipv4_rules(bpf: &mut Ebpf, ip_rules: &HashMap<String, IpRules>) -> anyhow::Result<()> {
let mut ipv4_map: LpmTrie<&mut MapData, LpmKeyIpv4, IpRule> = LpmTrie::try_from(
bpf.map_mut("IPV4_RULES")
.context("Failed to find IPV4_RULES map")?,
Expand Down Expand Up @@ -150,7 +153,7 @@ fn populate_ipv4_rules(bpf: &mut Ebpf, ip_rules: &HashMap<String, Rules>) -> any
Ok(())
}

fn prepare_ip_rule(rule: &Rules) -> anyhow::Result<IpRule> {
fn prepare_ip_rule(rule: &IpRules) -> anyhow::Result<IpRule> {
let mut ports = [0u16; 16];
for (i, &port) in rule.ports.iter().enumerate().take(16) {
ports[i] = port as u16;
Expand All @@ -165,7 +168,6 @@ fn prepare_ip_rule(rule: &Rules) -> anyhow::Result<IpRule> {
protocol: match rule.protocol {
Protocol::Tcp => 6,
Protocol::Udp => 17,
Protocol::Icmp => 1,
},
priority: rule.priority,
})
Expand Down

0 comments on commit 14c39c3

Please sign in to comment.