Add simple port fw logic
parent
273123abb7
commit
9e53687adf
@ -1,7 +1,80 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::mem;
|
||||
use memoffset::offset_of;
|
||||
|
||||
use aya_bpf::{
|
||||
bindings::xdp_action::{XDP_DROP, XDP_PASS},
|
||||
macros::{map, xdp},
|
||||
maps::HashMap,
|
||||
programs::XdpContext,
|
||||
};
|
||||
|
||||
mod net;
|
||||
use net::{iphdr, tcphdr};
|
||||
|
||||
use crate::net::ethhdr;
|
||||
|
||||
#[map]
|
||||
static mut BLOCK_PORTS: HashMap<u16, u16> = HashMap::with_max_entries(1024, 0);
|
||||
|
||||
#[xdp(name = "xdp_fw")]
|
||||
pub fn xdp_firewall(ctx: XdpContext) -> u32 {
|
||||
match try_xdp_firewall(ctx) {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => XDP_PASS,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_xdp_firewall(ctx: XdpContext) -> Result<u32, ()> {
|
||||
if let Some(port) = tcp_dest_port(&ctx)? {
|
||||
if block_port(port) {
|
||||
return Ok(XDP_DROP);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(XDP_PASS)
|
||||
}
|
||||
|
||||
fn tcp_dest_port(ctx: &XdpContext) -> Result<Option<u16>, ()> {
|
||||
let h_proto = u16::from_be(unsafe { *ptr_at(&ctx, offset_of!(ethhdr, h_proto))? });
|
||||
let ip_proto: u8 = unsafe { *ptr_at(&ctx, ETH_HDR_LEN + offset_of!(iphdr, protocol))? };
|
||||
|
||||
if h_proto != ETH_P_IP || ip_proto != IPPROTO_TCP {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let dest = u16::from_be(unsafe {
|
||||
*ptr_at(&ctx, ETH_HDR_LEN + IP_HDR_LEN + offset_of!(tcphdr, dest))?
|
||||
});
|
||||
|
||||
Ok(Some(dest))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn ptr_at<T>(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> {
|
||||
let start = ctx.data();
|
||||
let end = ctx.data_end();
|
||||
let len = mem::size_of::<T>();
|
||||
|
||||
if start + offset + len > end {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok((start + offset) as *const T)
|
||||
}
|
||||
|
||||
fn block_port(port: u16) -> bool {
|
||||
unsafe { BLOCK_PORTS.get(&port).is_some() }
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
const ETH_P_IP: u16 = 0x0800;
|
||||
const IPPROTO_TCP: u8 = 6;
|
||||
const ETH_HDR_LEN: usize = mem::size_of::<ethhdr>();
|
||||
const IP_HDR_LEN: usize = mem::size_of::<iphdr>();
|
||||
|
@ -0,0 +1,337 @@
|
||||
#![allow(non_camel_case_types, dead_code)]
|
||||
/* automatically generated by rust-bindgen 0.57.0 */
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct __BindgenBitfieldUnit<Storage> {
|
||||
pub storage: Storage,
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage> {
|
||||
#[inline]
|
||||
pub const fn new(storage: Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
}
|
||||
impl<Storage> __BindgenBitfieldUnit<Storage>
|
||||
where
|
||||
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn get_bit(&self, index: usize) -> bool {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = self.storage.as_ref()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
byte & mask == mask
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||
let byte_index = index / 8;
|
||||
let byte = &mut self.storage.as_mut()[byte_index];
|
||||
let bit_index = if cfg!(target_endian = "big") {
|
||||
7 - (index % 8)
|
||||
} else {
|
||||
index % 8
|
||||
};
|
||||
let mask = 1 << bit_index;
|
||||
if val {
|
||||
*byte |= mask;
|
||||
} else {
|
||||
*byte &= !mask;
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
let mut val = 0;
|
||||
for i in 0..(bit_width as usize) {
|
||||
if self.get_bit(i + bit_offset) {
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
val |= 1 << index;
|
||||
}
|
||||
}
|
||||
val
|
||||
}
|
||||
#[inline]
|
||||
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||
debug_assert!(bit_width <= 64);
|
||||
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||
for i in 0..(bit_width as usize) {
|
||||
let mask = 1 << i;
|
||||
let val_bit_is_set = val & mask == mask;
|
||||
let index = if cfg!(target_endian = "big") {
|
||||
bit_width as usize - 1 - i
|
||||
} else {
|
||||
i
|
||||
};
|
||||
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type __u8 = ::aya_bpf::cty::c_uchar;
|
||||
pub type __u16 = ::aya_bpf::cty::c_ushort;
|
||||
pub type __u32 = ::aya_bpf::cty::c_uint;
|
||||
pub type __be16 = __u16;
|
||||
pub type __be32 = __u32;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ethhdr {
|
||||
pub h_dest: [::aya_bpf::cty::c_uchar; 6usize],
|
||||
pub h_source: [::aya_bpf::cty::c_uchar; 6usize],
|
||||
pub h_proto: __be16,
|
||||
}
|
||||
pub type __sum16 = __u16;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct tcphdr {
|
||||
pub source: __be16,
|
||||
pub dest: __be16,
|
||||
pub seq: __be32,
|
||||
pub ack_seq: __be32,
|
||||
pub _bitfield_align_1: [u8; 0],
|
||||
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 2usize]>,
|
||||
pub window: __be16,
|
||||
pub check: __sum16,
|
||||
pub urg_ptr: __be16,
|
||||
}
|
||||
impl tcphdr {
|
||||
#[inline]
|
||||
pub fn res1(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(0usize, 4u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_res1(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(0usize, 4u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn doff(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(4usize, 4u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_doff(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(4usize, 4u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn fin(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_fin(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(8usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn syn(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(9usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_syn(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(9usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn rst(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(10usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_rst(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(10usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn psh(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(11usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_psh(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(11usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn ack(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(12usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_ack(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(12usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn urg(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(13usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_urg(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(13usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn ece(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(14usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_ece(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(14usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn cwr(&self) -> __u16 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(15usize, 1u8) as u16) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_cwr(&mut self, val: __u16) {
|
||||
unsafe {
|
||||
let val: u16 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(15usize, 1u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn new_bitfield_1(
|
||||
res1: __u16,
|
||||
doff: __u16,
|
||||
fin: __u16,
|
||||
syn: __u16,
|
||||
rst: __u16,
|
||||
psh: __u16,
|
||||
ack: __u16,
|
||||
urg: __u16,
|
||||
ece: __u16,
|
||||
cwr: __u16,
|
||||
) -> __BindgenBitfieldUnit<[u8; 2usize]> {
|
||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 2usize]> = Default::default();
|
||||
__bindgen_bitfield_unit.set(0usize, 4u8, {
|
||||
let res1: u16 = unsafe { ::core::mem::transmute(res1) };
|
||||
res1 as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(4usize, 4u8, {
|
||||
let doff: u16 = unsafe { ::core::mem::transmute(doff) };
|
||||
doff as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(8usize, 1u8, {
|
||||
let fin: u16 = unsafe { ::core::mem::transmute(fin) };
|
||||
fin as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(9usize, 1u8, {
|
||||
let syn: u16 = unsafe { ::core::mem::transmute(syn) };
|
||||
syn as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(10usize, 1u8, {
|
||||
let rst: u16 = unsafe { ::core::mem::transmute(rst) };
|
||||
rst as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(11usize, 1u8, {
|
||||
let psh: u16 = unsafe { ::core::mem::transmute(psh) };
|
||||
psh as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(12usize, 1u8, {
|
||||
let ack: u16 = unsafe { ::core::mem::transmute(ack) };
|
||||
ack as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(13usize, 1u8, {
|
||||
let urg: u16 = unsafe { ::core::mem::transmute(urg) };
|
||||
urg as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(14usize, 1u8, {
|
||||
let ece: u16 = unsafe { ::core::mem::transmute(ece) };
|
||||
ece as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(15usize, 1u8, {
|
||||
let cwr: u16 = unsafe { ::core::mem::transmute(cwr) };
|
||||
cwr as u64
|
||||
});
|
||||
__bindgen_bitfield_unit
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct iphdr {
|
||||
pub _bitfield_align_1: [u8; 0],
|
||||
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>,
|
||||
pub tos: __u8,
|
||||
pub tot_len: __be16,
|
||||
pub id: __be16,
|
||||
pub frag_off: __be16,
|
||||
pub ttl: __u8,
|
||||
pub protocol: __u8,
|
||||
pub check: __sum16,
|
||||
pub saddr: __be32,
|
||||
pub daddr: __be32,
|
||||
}
|
||||
impl iphdr {
|
||||
#[inline]
|
||||
pub fn ihl(&self) -> __u8 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(0usize, 4u8) as u8) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_ihl(&mut self, val: __u8) {
|
||||
unsafe {
|
||||
let val: u8 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(0usize, 4u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn version(&self) -> __u8 {
|
||||
unsafe { ::core::mem::transmute(self._bitfield_1.get(4usize, 4u8) as u8) }
|
||||
}
|
||||
#[inline]
|
||||
pub fn set_version(&mut self, val: __u8) {
|
||||
unsafe {
|
||||
let val: u8 = ::core::mem::transmute(val);
|
||||
self._bitfield_1.set(4usize, 4u8, val as u64)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn new_bitfield_1(ihl: __u8, version: __u8) -> __BindgenBitfieldUnit<[u8; 1usize]> {
|
||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default();
|
||||
__bindgen_bitfield_unit.set(0usize, 4u8, {
|
||||
let ihl: u8 = unsafe { ::core::mem::transmute(ihl) };
|
||||
ihl as u64
|
||||
});
|
||||
__bindgen_bitfield_unit.set(4usize, 4u8, {
|
||||
let version: u8 = unsafe { ::core::mem::transmute(version) };
|
||||
version as u64
|
||||
});
|
||||
__bindgen_bitfield_unit
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct udphdr {
|
||||
pub source: __be16,
|
||||
pub dest: __be16,
|
||||
pub len: __be16,
|
||||
pub check: __sum16,
|
||||
}
|
@ -1,9 +1,52 @@
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
process,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use aya::{
|
||||
maps::HashMap,
|
||||
programs::{Xdp, XdpFlags},
|
||||
Bpf, Btf,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = try_main() {
|
||||
eprintln!("error: {:#}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn try_main() -> Result<(), anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
// load the eBPF code
|
||||
let code = include_bytes!("../../target/bpfel-unknown-none/debug/{{ project-name }}").to_vec();
|
||||
let mut bpf = Bpf::load(&code, Btf::from_sys_fs().ok().as_ref())?;
|
||||
|
||||
// insert port 8080 in the list of ports to be blocked
|
||||
let mut map = HashMap::<_, u16, u16>::try_from(bpf.map_mut("BLOCK_PORTS")?)?;
|
||||
map.insert(8080, 1, 0)?;
|
||||
|
||||
// load the XDP program
|
||||
let prog: &mut Xdp = bpf.program_mut("xdp_fw")?.try_into()?;
|
||||
prog.load()?;
|
||||
prog.attach("eth0", XdpFlags::SKB_MODE)?;
|
||||
|
||||
// wait for SIGINT or SIGTERM
|
||||
Ok(loop_until_terminated())
|
||||
}
|
||||
|
||||
fn loop_until_terminated() {
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let r = running.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
r.store(false, Ordering::SeqCst);
|
||||
})
|
||||
.expect("Error setting signal handler");
|
||||
|
||||
while running.load(Ordering::SeqCst) {}
|
||||
println!("Exiting...");
|
||||
}
|
||||
|
Loading…
Reference in New Issue