Add simple port fw logic

pull/1/head
Alessandro Decina 4 years ago
parent 273123abb7
commit 9e53687adf

@ -6,6 +6,7 @@ edition = "2018"
[dependencies]
aya-bpf = { git = "http://github.com/alessandrod/aya", branch = "main" }
{{ project-name }}-common = { path = "../{{ project-name }}-common" }
memoffset = "0.6"
[[bin]]
name = "{{ project-name }}"

@ -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,
}

@ -8,6 +8,7 @@ publish = false
aya = { git = "https://github.com/alessandrod/aya", branch="main" }
{{project-name}}-common = { path = "../{{project-name}}-common", features=["userspace"] }
anyhow = "1.0.42"
ctrlc = "3.1"
[[bin]]
name = "{{project-name}}"

@ -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…
Cancel
Save