diff options
author | David Lönnhager <david.l@mullvad.net> | 2020-05-27 13:46:27 +0200 |
---|---|---|
committer | David Lönnhager <david.l@mullvad.net> | 2020-05-27 13:46:27 +0200 |
commit | 343727fdede3d7c2e6f5338d2267be41295859f1 (patch) | |
tree | 5279811faba137994c5175ac3b78ab7448721f3a | |
parent | 39982629ee8c4bc129f3a80b844d9d3d9486d5d7 (diff) | |
parent | accce17a2becb7310a91c8632551c48c49b67554 (diff) |
Merge branch 'add-reject-expr'
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | nftnl/src/expr/bitwise.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/cmp.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/counter.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/ct.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/immediate.rs | 104 | ||||
-rw-r--r-- | nftnl/src/expr/lookup.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/masquerade.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/meta.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/mod.rs | 6 | ||||
-rw-r--r-- | nftnl/src/expr/payload.rs | 4 | ||||
-rw-r--r-- | nftnl/src/expr/verdict.rs | 175 | ||||
-rw-r--r-- | nftnl/src/lib.rs | 2 | ||||
-rw-r--r-- | nftnl/src/rule.rs | 2 |
14 files changed, 203 insertions, 120 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8df81e0..d4e44d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added +- Add `Reject` verdict for responding with ICMP packets or TCP RST to the origin. ## [0.3.0] - 2020-04-20 diff --git a/nftnl/src/expr/bitwise.rs b/nftnl/src/expr/bitwise.rs index f017899..be2a0cf 100644 --- a/nftnl/src/expr/bitwise.rs +++ b/nftnl/src/expr/bitwise.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use crate::expr::cmp::ToSlice; use libc; use nftnl_sys::{ @@ -21,7 +21,7 @@ impl<M: ToSlice, X: ToSlice> Bitwise<M, X> { } impl<M: ToSlice, X: ToSlice> Expression for Bitwise<M, X> { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc( b"bitwise\0" as *const _ as *const c_char diff --git a/nftnl/src/expr/cmp.rs b/nftnl/src/expr/cmp.rs index 838c079..36bcc25 100644 --- a/nftnl/src/expr/cmp.rs +++ b/nftnl/src/expr/cmp.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use libc; use nftnl_sys::{ self as sys, @@ -59,7 +59,7 @@ impl<T: ToSlice> Cmp<T> { } impl<T: ToSlice> Expression for Cmp<T> { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(b"cmp\0" as *const _ as *const c_char)); diff --git a/nftnl/src/expr/counter.rs b/nftnl/src/expr/counter.rs index 822a714..3f1415c 100644 --- a/nftnl/src/expr/counter.rs +++ b/nftnl/src/expr/counter.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use nftnl_sys::{self as sys, libc::c_char}; /// A counter expression adds a counter to the rule that is incremented to count number of packets @@ -6,7 +6,7 @@ use nftnl_sys::{self as sys, libc::c_char}; pub struct Counter; impl Expression for Counter { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { try_alloc!(unsafe { sys::nftnl_expr_alloc(b"counter\0" as *const _ as *const c_char) }) } } diff --git a/nftnl/src/expr/ct.rs b/nftnl/src/expr/ct.rs index 65b47bc..97e61a3 100644 --- a/nftnl/src/expr/ct.rs +++ b/nftnl/src/expr/ct.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use libc; use nftnl_sys::{self as sys, libc::c_char}; @@ -27,7 +27,7 @@ impl Conntrack { } impl Expression for Conntrack { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc(b"ct\0" as *const _ as *const c_char)); diff --git a/nftnl/src/expr/immediate.rs b/nftnl/src/expr/immediate.rs index 0e89abc..f0e0c2c 100644 --- a/nftnl/src/expr/immediate.rs +++ b/nftnl/src/expr/immediate.rs @@ -1,7 +1,7 @@ -use super::Expression; +use super::{Expression, Rule}; use libc; use nftnl_sys::{self as sys, libc::{c_char, c_void}}; -use std::{ffi::{CStr, CString}, mem::size_of_val}; +use std::mem::size_of_val; /// An immediate expression. Used to set immediate data. /// Verdicts are handled separately by [Verdict]. @@ -11,7 +11,7 @@ pub struct Immediate<T> { } impl<T> Expression for Immediate<T> { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc( b"immediate\0" as *const _ as *const c_char @@ -41,101 +41,3 @@ macro_rules! nft_expr_immediate { $crate::expr::Immediate { data: $value } }; } - -/// A verdict expression. In the background actually an "Immediate" expression in nftnl terms, -/// but here it's simplified to only represent a verdict. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum Verdict { - /// Silently drop the packet. - Drop, - /// Accept the packet and let it pass. - Accept, - Queue, - Continue, - Break, - Jump { - chain: CString, - }, - Goto { - chain: CString, - }, - Return, -} - -impl Verdict { - fn verdict_const(&self) -> i32 { - match *self { - Verdict::Drop => libc::NF_DROP, - Verdict::Accept => libc::NF_ACCEPT, - Verdict::Queue => libc::NF_QUEUE, - Verdict::Continue => libc::NFT_CONTINUE, - Verdict::Break => libc::NFT_BREAK, - Verdict::Jump { .. } => libc::NFT_JUMP, - Verdict::Goto { .. } => libc::NFT_GOTO, - Verdict::Return => libc::NFT_RETURN, - } - } - - fn chain(&self) -> Option<&CStr> { - match *self { - Verdict::Jump { ref chain } => Some(chain.as_c_str()), - Verdict::Goto { ref chain } => Some(chain.as_c_str()), - _ => None, - } - } -} - -impl Expression for Verdict { - fn to_expr(&self) -> *mut sys::nftnl_expr { - unsafe { - let expr = try_alloc!(sys::nftnl_expr_alloc( - b"immediate\0" as *const _ as *const c_char - )); - - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_IMM_DREG as u16, - libc::NFT_REG_VERDICT as u32, - ); - - if let Some(chain) = self.chain() { - sys::nftnl_expr_set_str(expr, sys::NFTNL_EXPR_IMM_CHAIN as u16, chain.as_ptr()); - } - sys::nftnl_expr_set_u32( - expr, - sys::NFTNL_EXPR_IMM_VERDICT as u16, - self.verdict_const() as u32, - ); - - expr - } - } -} - -#[macro_export] -macro_rules! nft_expr_verdict { - (drop) => { - $crate::expr::Verdict::Drop - }; - (accept) => { - $crate::expr::Verdict::Accept - }; - (queue) => { - $crate::expr::Verdict::Queue - }; - (continue) => { - $crate::expr::Verdict::Continue - }; - (break) => { - $crate::expr::Verdict::Break - }; - (jump $chain:expr) => { - $crate::expr::Verdict::Jump { chain: $chain } - }; - (goto $chain:expr) => { - $crate::expr::Verdict::Goto { chain: $chain } - }; - (return) => { - $crate::expr::Verdict::Return - }; -} diff --git a/nftnl/src/expr/lookup.rs b/nftnl/src/expr/lookup.rs index 08fccf5..449d52a 100644 --- a/nftnl/src/expr/lookup.rs +++ b/nftnl/src/expr/lookup.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use crate::set::Set; use libc; use nftnl_sys::{self as sys, libc::c_char}; @@ -19,7 +19,7 @@ impl Lookup { } impl Expression for Lookup { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc( b"lookup\0" as *const _ as *const c_char diff --git a/nftnl/src/expr/masquerade.rs b/nftnl/src/expr/masquerade.rs index e6684c2..d136a65 100644 --- a/nftnl/src/expr/masquerade.rs +++ b/nftnl/src/expr/masquerade.rs @@ -1,11 +1,11 @@ -use super::Expression; +use super::{Expression, Rule}; use nftnl_sys::{self as sys, libc::c_char}; /// Sets the source IP to that of the output interface. pub struct Masquerade; impl Expression for Masquerade { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { try_alloc!(unsafe { sys::nftnl_expr_alloc(b"masq\0" as *const _ as *const c_char) }) } } diff --git a/nftnl/src/expr/meta.rs b/nftnl/src/expr/meta.rs index 8b3218d..49a3c3f 100644 --- a/nftnl/src/expr/meta.rs +++ b/nftnl/src/expr/meta.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use libc; use nftnl_sys::{self as sys, libc::c_char}; @@ -52,7 +52,7 @@ impl Meta { } impl Expression for Meta { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc( b"meta\0" as *const _ as *const c_char diff --git a/nftnl/src/expr/mod.rs b/nftnl/src/expr/mod.rs index 84d7035..e4c4adb 100644 --- a/nftnl/src/expr/mod.rs +++ b/nftnl/src/expr/mod.rs @@ -3,13 +3,14 @@ //! //! [`Rule`]: struct.Rule.html +use super::rule::Rule; use nftnl_sys as sys; /// Trait for every safe wrapper of an nftables expression. pub trait Expression { /// Allocates and returns the low level `nftnl_expr` representation of this expression. /// The caller to this method is responsible for freeing the expression. - fn to_expr(&self) -> *mut sys::nftnl_expr; + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr; } mod bitwise; @@ -39,6 +40,9 @@ pub use self::meta::*; mod payload; pub use self::payload::*; +mod verdict; +pub use self::verdict::*; + #[macro_export(local_inner_macros)] macro_rules! nft_expr { (bitwise mask $mask:expr,xor $xor:expr) => { diff --git a/nftnl/src/expr/payload.rs b/nftnl/src/expr/payload.rs index 83ce7a7..45e5955 100644 --- a/nftnl/src/expr/payload.rs +++ b/nftnl/src/expr/payload.rs @@ -1,4 +1,4 @@ -use super::Expression; +use super::{Expression, Rule}; use libc; use nftnl_sys::{self as sys, libc::c_char}; @@ -47,7 +47,7 @@ impl HeaderField for Payload { } impl Expression for Payload { - fn to_expr(&self) -> *mut sys::nftnl_expr { + fn to_expr(&self, _rule: &Rule) -> *mut sys::nftnl_expr { unsafe { let expr = try_alloc!(sys::nftnl_expr_alloc( b"payload\0" as *const _ as *const c_char diff --git a/nftnl/src/expr/verdict.rs b/nftnl/src/expr/verdict.rs new file mode 100644 index 0000000..1395a94 --- /dev/null +++ b/nftnl/src/expr/verdict.rs @@ -0,0 +1,175 @@ +use super::{Expression, Rule}; +use crate::ProtoFamily; +use nftnl_sys::{ + self as sys, + libc::{self, c_char}, +}; +use std::ffi::{CStr, CString}; + +/// A verdict expression. In the background, this is usually an "Immediate" expression in nftnl +/// terms, but here it is simplified to only represent a verdict. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum Verdict { + /// Silently drop the packet. + Drop, + /// Accept the packet and let it pass. + Accept, + /// Reject the packet and return a message. + Reject(RejectionType), + Queue, + Continue, + Break, + Jump { + chain: CString, + }, + Goto { + chain: CString, + }, + Return, +} + +/// The type of rejection message sent by the Reject verdict. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum RejectionType { + /// Return an ICMP unreachable packet + Icmp(IcmpCode), + /// Reject by sending a TCP RST packet + TcpRst, +} + +impl RejectionType { + fn to_raw(&self, family: ProtoFamily) -> u32 { + use libc::*; + let value = match *self { + RejectionType::Icmp(..) => match family { + ProtoFamily::Bridge | ProtoFamily::Inet => NFT_REJECT_ICMPX_UNREACH, + _ => NFT_REJECT_ICMP_UNREACH, + }, + RejectionType::TcpRst => NFT_REJECT_TCP_RST, + }; + value as u32 + } +} + +/// An ICMP reject code. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[repr(u8)] +pub enum IcmpCode { + NoRoute = libc::NFT_REJECT_ICMPX_NO_ROUTE as u8, + PortUnreach = libc::NFT_REJECT_ICMPX_PORT_UNREACH as u8, + HostUnreach = libc::NFT_REJECT_ICMPX_HOST_UNREACH as u8, + AdminProhibited = libc::NFT_REJECT_ICMPX_ADMIN_PROHIBITED as u8, +} + +impl Verdict { + unsafe fn to_immediate_expr(&self, immediate_const: i32) -> *mut sys::nftnl_expr { + let expr = try_alloc!(sys::nftnl_expr_alloc( + b"immediate\0" as *const _ as *const c_char + )); + + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_IMM_DREG as u16, + libc::NFT_REG_VERDICT as u32, + ); + + if let Some(chain) = self.chain() { + sys::nftnl_expr_set_str(expr, sys::NFTNL_EXPR_IMM_CHAIN as u16, chain.as_ptr()); + } + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_IMM_VERDICT as u16, + immediate_const as u32, + ); + + expr + } + + unsafe fn to_reject_expr( + &self, + reject_type: RejectionType, + family: ProtoFamily, + ) -> *mut sys::nftnl_expr { + let expr = try_alloc!(sys::nftnl_expr_alloc( + b"reject\0" as *const _ as *const c_char + )); + + sys::nftnl_expr_set_u32( + expr, + sys::NFTNL_EXPR_REJECT_TYPE as u16, + reject_type.to_raw(family), + ); + + let reject_code = match reject_type { + RejectionType::Icmp(code) => code as u8, + RejectionType::TcpRst => 0, + }; + + sys::nftnl_expr_set_u8(expr, sys::NFTNL_EXPR_REJECT_CODE as u16, reject_code); + + expr + } + + fn chain(&self) -> Option<&CStr> { + match *self { + Verdict::Jump { ref chain } => Some(chain.as_c_str()), + Verdict::Goto { ref chain } => Some(chain.as_c_str()), + _ => None, + } + } +} + +impl Expression for Verdict { + fn to_expr(&self, rule: &Rule) -> *mut sys::nftnl_expr { + let immediate_const = match *self { + Verdict::Drop => libc::NF_DROP, + Verdict::Accept => libc::NF_ACCEPT, + Verdict::Queue => libc::NF_QUEUE, + Verdict::Continue => libc::NFT_CONTINUE, + Verdict::Break => libc::NFT_BREAK, + Verdict::Jump { .. } => libc::NFT_JUMP, + Verdict::Goto { .. } => libc::NFT_GOTO, + Verdict::Return => libc::NFT_RETURN, + Verdict::Reject(reject_type) => { + return unsafe { + self.to_reject_expr(reject_type, rule.get_chain().get_table().get_family()) + } + } + }; + unsafe { self.to_immediate_expr(immediate_const) } + } +} + +#[macro_export] +macro_rules! nft_expr_verdict { + (drop) => { + $crate::expr::Verdict::Drop + }; + (accept) => { + $crate::expr::Verdict::Accept + }; + (reject icmp $code:expr) => { + $crate::expr::Verdict::Reject(RejectionType::Icmp($code)) + }; + (reject tcp-rst) => { + $crate::expr::Verdict::Reject(RejectionType::TcpRst) + }; + (queue) => { + $crate::expr::Verdict::Queue + }; + (continue) => { + $crate::expr::Verdict::Continue + }; + (break) => { + $crate::expr::Verdict::Break + }; + (jump $chain:expr) => { + $crate::expr::Verdict::Jump { chain: $chain } + }; + (goto $chain:expr) => { + $crate::expr::Verdict::Goto { chain: $chain } + }; + (return) => { + $crate::expr::Verdict::Return + }; +} diff --git a/nftnl/src/lib.rs b/nftnl/src/lib.rs index e47a600..31375bf 100644 --- a/nftnl/src/lib.rs +++ b/nftnl/src/lib.rs @@ -87,7 +87,7 @@ pub enum MsgType { } /// Denotes a protocol. Used to specify which protocol a table or set belongs to. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[repr(u16)] pub enum ProtoFamily { Unspec = libc::NFPROTO_UNSPEC as u16, diff --git a/nftnl/src/rule.rs b/nftnl/src/rule.rs index 77a0e1c..18f1084 100644 --- a/nftnl/src/rule.rs +++ b/nftnl/src/rule.rs @@ -53,7 +53,7 @@ impl<'a> Rule<'a> { /// As soon as an expression does not match the packet it's being evaluated for, evaluation /// stops and the packet is evaluated against the next rule in the chain. pub fn add_expr(&mut self, expr: &impl Expression) { - unsafe { sys::nftnl_rule_add_expr(self.rule, expr.to_expr()) } + unsafe { sys::nftnl_rule_add_expr(self.rule, expr.to_expr(self)) } } /// Returns a reference to the [`Chain`] this rule lives in. |