Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
// Point rust-analyzer at the actual crate(s) in this workspace
"rust-analyzer.linkedProjects": [
"synapse/Cargo.toml"
"Cargo.toml"
],
// Enable all features if your crate uses optional features
"rust-analyzer.cargo.allFeatures": true,
// Run clippy on save for quick lint feedback (optional)
"rust-analyzer.checkOnSave.command": "clippy",
// Use the local toolchain when available
"rust-analyzer.rustc.source": "discover"
}
}
85 changes: 85 additions & 0 deletions src/bpf/filter.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ struct tcp_syn_stats {
__u64 last_reset;
};

struct src_port_key_v4 {
__be32 addr;
__be16 port;
};

struct src_port_key_v6 {
__u8 addr[16];
__be16 port;
};

// IPv4 maps: permanently banned and recently banned
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
Expand Down Expand Up @@ -222,6 +232,21 @@ struct {
__type(value, __u64); // Drop count
} dropped_ipv6_addresses SEC(".maps");


struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
__type(key, struct src_port_key_v4);
__type(value, __u8);
} banned_ipv4_address_ports SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
__type(key, struct src_port_key_v6);
__type(value, __u8);
} banned_ipv6_address_ports SEC(".maps");

/*
* Helper for bounds checking and advancing a cursor.
*
Expand Down Expand Up @@ -668,6 +693,35 @@ int arxignis_xdp_filter(struct xdp_md *ctx)
}
}

// Check IPv4 port bans
if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) {
void *port_cursor = cursor;
__be16 src_port = 0;

if (iph->protocol == IPPROTO_TCP) {
struct tcphdr *tcph_tmp = parse_and_advance(&port_cursor, data_end, sizeof(*tcph_tmp));
if (!tcph_tmp)
return XDP_PASS;
src_port = tcph_tmp->source;
} else {
struct udphdr *udph_tmp = parse_and_advance(&port_cursor, data_end, sizeof(*udph_tmp));
if (!udph_tmp)
return XDP_PASS;
src_port = udph_tmp->source;
}

struct src_port_key_v4 port_key = {
.addr = iph->saddr,
.port = src_port,
};

if (bpf_map_lookup_elem(&banned_ipv4_address_ports, &port_key)) {
increment_total_packets_dropped();
increment_dropped_ipv4_address(iph->saddr);
return XDP_DROP;
}
}

return XDP_PASS;
}
else if (h_proto == bpf_htons(ETH_P_IPV6)) {
Expand Down Expand Up @@ -847,6 +901,37 @@ int arxignis_xdp_filter(struct xdp_md *ctx)
}
}

// Check IPv6 port bans
if (ip6h->nexthdr == IPPROTO_TCP || ip6h->nexthdr == IPPROTO_UDP) {
void *port_cursor = cursor;
__be16 src_port = 0;

if (ip6h->nexthdr == IPPROTO_TCP) {
struct tcphdr *tcph_tmp = parse_and_advance(&port_cursor, data_end, sizeof(*tcph_tmp));
if (!tcph_tmp)
return XDP_PASS;
src_port = tcph_tmp->source;
} else {
struct udphdr *udph_tmp = parse_and_advance(&port_cursor, data_end, sizeof(*udph_tmp));
if (!udph_tmp)
return XDP_PASS;
src_port = udph_tmp->source;
}

struct src_port_key_v6 port_key6 = {0};
#pragma unroll
for (int i = 0; i < 16; i++) {
port_key6.addr[i] = ((__u8 *)&ip6h->saddr)[i];
}
port_key6.port = src_port;

if (bpf_map_lookup_elem(&banned_ipv6_address_ports, &port_key6)) {
increment_total_packets_dropped();
increment_dropped_ipv6_address(ip6h->saddr);
return XDP_DROP;
}
}

return XDP_PASS;
}

Expand Down
135 changes: 110 additions & 25 deletions src/firewall.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{error::Error, net::{Ipv4Addr, Ipv6Addr}};
use std::{
error::Error,
net::{Ipv4Addr, Ipv6Addr},
};

use libbpf_rs::{MapCore, MapFlags};

Expand All @@ -23,6 +26,14 @@ pub trait Firewall {
fn unblock_tcp_fingerprint_v6(&mut self, fingerprint: &str) -> Result<(), Box<dyn Error>>;
fn is_tcp_fingerprint_blocked(&self, fingerprint: &str) -> Result<bool, Box<dyn Error>>;
fn is_tcp_fingerprint_blocked_v6(&self, fingerprint: &str) -> Result<bool, Box<dyn Error>>;

fn ban_ipv4_port(&mut self, ip: Ipv4Addr, port: u16) -> Result<(), Box<dyn Error>>;
fn unban_ipv4_port(&mut self, ip: Ipv4Addr, port: u16) -> Result<(), Box<dyn Error>>;
fn is_ipv4_port_banned(&mut self, ip: Ipv4Addr, port: u16) -> Result<bool, Box<dyn Error>>;

fn ban_ipv6_port(&mut self, ip: Ipv6Addr, port: u16) -> Result<(), Box<dyn Error>>;
fn unban_ipv6_port(&mut self, ip: Ipv6Addr, port: u16) -> Result<(), Box<dyn Error>>;
fn is_ipv6_port_banned(&mut self, ip: Ipv6Addr, port: u16) -> Result<bool, Box<dyn Error>>;
}

pub struct MOATFirewall<'a> {
Expand All @@ -40,11 +51,11 @@ impl<'a> MOATFirewall<'a> {
fn fingerprint_to_bytes(fingerprint: &str) -> Result<[u8; 14], Box<dyn Error>> {
let mut bytes = [0u8; 14];
let fp_bytes = fingerprint.as_bytes();

// Copy up to 14 bytes
let copy_len = std::cmp::min(fp_bytes.len(), 14);
bytes[..copy_len].copy_from_slice(&fp_bytes[..copy_len]);

Ok(bytes)
}
}
Expand Down Expand Up @@ -106,10 +117,11 @@ impl<'a> Firewall for MOATFirewall<'a> {
let ip_bytes = &bpf_utils::convert_ipv6_into_bpf_map_key_bytes(ip, prefixlen);
let flag = 1_u8;

self.skel
.maps
.recently_banned_ips_v6
.update(ip_bytes, &flag.to_le_bytes(), MapFlags::ANY)?;
self.skel.maps.recently_banned_ips_v6.update(
ip_bytes,
&flag.to_le_bytes(),
MapFlags::ANY,
)?;

Ok(())
}
Expand Down Expand Up @@ -157,10 +169,11 @@ impl<'a> Firewall for MOATFirewall<'a> {
let fp_bytes = Self::fingerprint_to_bytes(fingerprint)?;
let flag = 1_u8;

self.skel
.maps
.blocked_tcp_fingerprints
.update(&fp_bytes, &flag.to_le_bytes(), MapFlags::ANY)?;
self.skel.maps.blocked_tcp_fingerprints.update(
&fp_bytes,
&flag.to_le_bytes(),
MapFlags::ANY,
)?;

log::info!("Blocked TCP fingerprint (IPv4): {}", fingerprint);
Ok(())
Expand All @@ -169,10 +182,7 @@ impl<'a> Firewall for MOATFirewall<'a> {
fn unblock_tcp_fingerprint(&mut self, fingerprint: &str) -> Result<(), Box<dyn Error>> {
let fp_bytes = Self::fingerprint_to_bytes(fingerprint)?;

self.skel
.maps
.blocked_tcp_fingerprints
.delete(&fp_bytes)?;
self.skel.maps.blocked_tcp_fingerprints.delete(&fp_bytes)?;

log::info!("Unblocked TCP fingerprint (IPv4): {}", fingerprint);
Ok(())
Expand All @@ -182,10 +192,11 @@ impl<'a> Firewall for MOATFirewall<'a> {
let fp_bytes = Self::fingerprint_to_bytes(fingerprint)?;
let flag = 1_u8;

self.skel
.maps
.blocked_tcp_fingerprints_v6
.update(&fp_bytes, &flag.to_le_bytes(), MapFlags::ANY)?;
self.skel.maps.blocked_tcp_fingerprints_v6.update(
&fp_bytes,
&flag.to_le_bytes(),
MapFlags::ANY,
)?;

log::info!("Blocked TCP fingerprint (IPv6): {}", fingerprint);
Ok(())
Expand All @@ -211,10 +222,9 @@ impl<'a> Firewall for MOATFirewall<'a> {
.maps
.blocked_tcp_fingerprints
.lookup(&fp_bytes, MapFlags::ANY)?
&& val[0] == 1_u8
{
if val[0] == 1_u8 {
return Ok(true);
}
return Ok(true);
}

Ok(false)
Expand All @@ -228,10 +238,85 @@ impl<'a> Firewall for MOATFirewall<'a> {
.maps
.blocked_tcp_fingerprints_v6
.lookup(&fp_bytes, MapFlags::ANY)?
&& val[0] == 1_u8
{
if val[0] == 1_u8 {
return Ok(true);
}
return Ok(true);
}

Ok(false)
}

fn ban_ipv4_port(&mut self, ip: Ipv4Addr, port: u16) -> Result<(), Box<dyn Error>> {
let key_bytes = bpf_utils::convert_ip_port_into_bpf_map_key_bytes(ip, port);

self.skel
.maps
.banned_ipv4_address_ports
.update(&key_bytes, &[1u8], MapFlags::ANY)?;

Ok(())
}

fn unban_ipv4_port(&mut self, ip: Ipv4Addr, port: u16) -> Result<(), Box<dyn Error>> {
let key_bytes = bpf_utils::convert_ip_port_into_bpf_map_key_bytes(ip, port);

self.skel
.maps
.banned_ipv4_address_ports
.delete(&key_bytes)?;

Ok(())
}

fn is_ipv4_port_banned(&mut self, ip: Ipv4Addr, port: u16) -> Result<bool, Box<dyn Error>> {
let key_bytes = bpf_utils::convert_ip_port_into_bpf_map_key_bytes(ip, port);

let res = self
.skel
.maps
.banned_ipv4_address_ports
.lookup(&key_bytes, MapFlags::ANY)?;

if res.is_some() {
return Ok(true);
}

Ok(false)
}

fn ban_ipv6_port(&mut self, ip: Ipv6Addr, port: u16) -> Result<(), Box<dyn Error>> {
let key_bytes = bpf_utils::convert_ipv6_port_into_map_key_bytes(ip, port);

self.skel
.maps
.banned_ipv6_address_ports
.update(&key_bytes, &[1u8], MapFlags::ANY)?;

Ok(())
}

fn unban_ipv6_port(&mut self, ip: Ipv6Addr, port: u16) -> Result<(), Box<dyn Error>> {
let key_bytes = bpf_utils::convert_ipv6_port_into_map_key_bytes(ip, port);

self.skel
.maps
.banned_ipv6_address_ports
.delete(&key_bytes)?;

Ok(())
}

fn is_ipv6_port_banned(&mut self, ip: Ipv6Addr, port: u16) -> Result<bool, Box<dyn Error>> {
let key_bytes = bpf_utils::convert_ipv6_port_into_map_key_bytes(ip, port);

let res = self
.skel
.maps
.banned_ipv6_address_ports
.lookup(&key_bytes, MapFlags::ANY)?;

if res.is_some() {
return Ok(true);
}

Ok(false)
Expand Down
Loading