Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ byteorder = "1.3"
derive_builder = "0.20"
getset = "0.1.2"
libc = "0.2.174"
linux-raw-sys = {version = "0.12.1", features = ["netlink"]}
log = "0.4"

[dependencies.neli-proc-macros]
Expand Down
126 changes: 126 additions & 0 deletions examples/fwmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use neli::{
consts::{
nl::{NlmF, Nlmsg},
rtnl::{Frattr, Frf, RtAddrFamily, Rtm},
socket::NlFamily,
},
err::Nlmsgerr,
nl::{NlPayload, NlmsghdrBuilder},
rtnl::{FibmsgBuilder, RtattrBuilder},
socket::synchronous::NlSocketHandle,
types::RtBuffer,
utils::Groups,
};

/// Must either have network permissions (setcap) or run as sudo
///
/// Will make the following rule:
/// [PRIORITY]: not from all fwmark 0xca6c lookup 246813579
///
/// Check via: ip rule show
/// Delete the rule via: sudo ip rule del priority [PRIORITY]
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

let socket = NlSocketHandle::connect(NlFamily::Route, None, Groups::empty())?;
socket.enable_ext_ack(true)?;
socket.enable_strict_checking(true)?;

let mut attrs = RtBuffer::new();
attrs.push(
RtattrBuilder::default()
.rta_type(Frattr::Fwmark)
.rta_payload(51820)
.build()?,
);

attrs.push(
RtattrBuilder::default()
.rta_type(Frattr::Table)
// avoid collisions with common table names
.rta_payload(246813579)
.build()?,
);

attrs.push(
RtattrBuilder::default()
.rta_type(Frattr::Priority)
.rta_payload(32765)
.build()?,
);
let attrs_clone = attrs.clone();

let fibmsg = FibmsgBuilder::default()
.fib_family(RtAddrFamily::Inet)
.fib_dst_len(0)
.fib_src_len(0)
.fib_tos(0)
.fib_flags(Frf::INVERT)
.fib_table(neli::consts::rtnl::RtTable::Unspec)
.fib_action(neli::consts::rtnl::FrAct::FrActToTbl)
.rtattrs(attrs)
.build()?;

let nlmsg = NlmsghdrBuilder::default()
.nl_type(Rtm::Newrule)
.nl_flags(NlmF::REQUEST | NlmF::ACK | NlmF::CREATE)
.nl_payload(NlPayload::Payload(fibmsg))
.build()?;

socket.send(&nlmsg)?;

if let Ok(messages) = socket.recv::<Nlmsg, Nlmsgerr<Rtm>>() {
for msg in messages.0 {
match msg {
Ok(val) => {
if *val.nl_type() == Nlmsg::Error {
if let NlPayload::Ack(err) = val.nl_payload() {
if *err.error() == 0 {
println!("Successfully created routing rule");
} else {
eprintln!(
"Failed to create routing rule with error code: {}",
err.error()
);
}
} else {
eprintln!("Received an error message with an unexpected payload.");
}
}
}
Err(e) => {
eprintln!("Error: Have you set network permissions for the file?");
return Err(Box::new(e));
}
}
}
} else {
eprintln!("Failed to receive acknowledgment for Newrule.");
}

// delete the table we just made
println!("\nDeleting the routing rule...");

let fibmsg = FibmsgBuilder::default()
.fib_family(RtAddrFamily::Inet)
.fib_dst_len(0)
.fib_src_len(0)
.fib_tos(0)
.fib_flags(Frf::INVERT)
.fib_table(neli::consts::rtnl::RtTable::Unspec)
.fib_action(neli::consts::rtnl::FrAct::FrActToTbl)
.rtattrs(attrs_clone)
.build()?;

let nlmsg = NlmsghdrBuilder::default()
.nl_type(Rtm::Delrule)
.nl_flags(NlmF::REQUEST | NlmF::ACK)
.nl_payload(NlPayload::Payload(fibmsg))
.build()?;

// comment this out to see
// the rule get created
socket.send(&nlmsg)?;

Ok(())
}
75 changes: 75 additions & 0 deletions src/consts/rtnl.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use linux_raw_sys::netlink;
use neli_proc_macros::neli_enum;

use crate as neli;
Expand Down Expand Up @@ -93,6 +94,7 @@ impl_trait!(
IflaInfo,
IflaVlan,
IflaVlanQos,
Frattr,
);

/// Enum usable with [`Rtattr`][crate::rtnl::Rtattr] field,
Expand Down Expand Up @@ -378,6 +380,64 @@ pub enum Ifa {
Flags = libc::IFA_FLAGS,
}

/// Enum usable with [`Rtattr`][crate::rtnl::Rtattr] field, `rta_type`.
///
/// Values are fib rule attributes. Used with
/// [`Fibmsg`][crate::rtnl::Fibmsg].
#[allow(missing_docs)]
#[neli_enum(serialized_type = "u16")]
pub enum Frattr {
Unspec = netlink::FRA_UNSPEC as u16,
Dst = netlink::FRA_DST as u16,
Src = netlink::FRA_SRC as u16,
Iifname = netlink::FRA_IIFNAME as u16,
Ifname = netlink::FRA_IIFNAME as u16,
Goto = netlink::FRA_GOTO as u16,
Unused2 = netlink::FRA_UNUSED2 as u16,
Priority = netlink::FRA_PRIORITY as u16,
Unused3 = netlink::FRA_UNUSED3 as u16,
Unused4 = netlink::FRA_UNUSED4 as u16,
Unused5 = netlink::FRA_UNUSED5 as u16,
Fwmark = netlink::FRA_FWMARK as u16,
Flow = netlink::FRA_FLOW as u16,
TunId = netlink::FRA_TUN_ID as u16,
SuppressIfgroup = netlink::FRA_SUPPRESS_IFGROUP as u16,
SuppressPrefixlen = netlink::FRA_SUPPRESS_PREFIXLEN as u16,
Table = netlink::FRA_TABLE as u16,
Fwmask = netlink::FRA_FWMASK as u16,
Oifname = netlink::FRA_OIFNAME as u16,
Pad = netlink::FRA_PAD as u16,
L3mdev = netlink::FRA_L3MDEV as u16,
UidRange = netlink::FRA_UID_RANGE as u16,
Protocol = netlink::FRA_PROTOCOL as u16,
IpProto = netlink::FRA_IP_PROTO as u16,
SportRange = netlink::FRA_SPORT_RANGE as u16,
DportRange = netlink::FRA_DPORT_RANGE as u16,
Dscp = netlink::FRA_DSCP as u16,
Flowlabel = netlink::FRA_FLOWLABEL as u16,
FlowlabelMask = netlink::FRA_FLOWLABEL_MASK as u16,
SportMask = netlink::FRA_SPORT_MASK as u16,
DportMask = netlink::FRA_DPORT_MASK as u16,
DscpMask = netlink::FRA_DSCP_MASK as u16,
Max = netlink::__FRA_MAX as u16,
}

/// Action for a FIB rule.
#[allow(missing_docs)]
#[neli_enum(serialized_type = "u8")]
pub enum FrAct {
Unspec = netlink::FR_ACT_UNSPEC as u8,
FrActToTbl = netlink::FR_ACT_TO_TBL as u8,
FrActGoto = netlink::FR_ACT_GOTO as u8,
FrActNop = netlink::FR_ACT_NOP as u8,
FrActRes3 = netlink::FR_ACT_RES3 as u8,
FrActRes4 = netlink::FR_ACT_RES4 as u8,
FrActBlackhole = netlink::FR_ACT_BLACKHOLE as u8,
FrActUnreachable = netlink::FR_ACT_UNREACHABLE as u8,
FrActProhibit = netlink::FR_ACT_PROHIBIT as u8,
FrActMax = netlink::__FR_ACT_MAX as u8,
}

impl_flags!(
/// Values for `ifi_flags` in
/// [`Ifinfomsg`][crate::rtnl::Ifinfomsg].
Expand Down Expand Up @@ -479,3 +539,18 @@ impl_flags!(
BRIDGE_BINDING = 0x10,
}
);

impl_flags!(
/// Values for `fib_flags` in
/// [`Fibmsg`][crate::rtnl::Fibmsg].
#[allow(missing_docs)]
pub Frf: u32 {
PERMANENT = netlink::FIB_RULE_PERMANENT,
INVERT = netlink::FIB_RULE_INVERT,
UNRESOLVED = netlink::FIB_RULE_UNRESOLVED,
IIF_DETACHED = netlink::FIB_RULE_IIF_DETACHED,
DEV_DETACHED = netlink::FIB_RULE_DEV_DETACHED,
OIF_DETACHED = netlink::FIB_RULE_OIF_DETACHED,
FIND_SADDR = netlink::FIB_RULE_FIND_SADDR,
}
);
41 changes: 41 additions & 0 deletions src/rtnl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,47 @@ pub struct Tcmsg {
rtattrs: RtBuffer<Tca, Buffer>,
}

/// Routing rule message
#[derive(Builder, Getters, Debug, Size, ToBytes, FromBytesWithInput, Header)]
#[builder(pattern = "owned")]
pub struct Fibmsg {
/// Address family
#[getset(get = "pub")]
fib_family: RtAddrFamily,
/// Length of destination prefix
#[getset(get = "pub")]
fib_dst_len: libc::c_uchar,
/// Length of source prefix
#[getset(get = "pub")]
fib_src_len: libc::c_uchar,
/// Type of service
#[getset(get = "pub")]
fib_tos: libc::c_uchar,
/// Routing table identifier
#[getset(get = "pub")]
fib_table: RtTable,
/// Padding
#[builder(setter(skip))]
#[builder(default = "0")]
pad1: u8,
/// Padding
#[builder(setter(skip))]
#[builder(default = "0")]
pad2: u8,
/// Rule action
#[getset(get = "pub")]
fib_action: FrAct,
/// Rule flags
#[getset(get = "pub")]
#[builder(default = "Frf::empty()")]
fib_flags: Frf,
/// Payload of [`Frattr`]s
#[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
#[getset(get = "pub")]
#[builder(default = "RtBuffer::new()")]
rtattrs: RtBuffer<Frattr, Buffer>,
}

/// Struct representing VLAN Flags
#[derive(Builder, Getters, Debug, Size, ToBytes, FromBytes)]
#[builder(pattern = "owned")]
Expand Down
Loading