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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ description = "netlink packet types for the netfilter subprotocol"

[dependencies]
netlink-packet-core = { version = "0.8.1" }
bitflags = "2.3"
bitflags = "2.10.0"
libc = "0.2.77"
derive_more = "0.99.16"

Expand Down
13 changes: 8 additions & 5 deletions examples/nflog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
// 2) build the example: cargo build --example nflog
// 3) run it as root: sudo ../target/debug/examples/nflog

use std::{net::Ipv4Addr, time::Duration};
use std::time::Duration;

use netlink_packet_core::{parse_ip, NetlinkMessage, NetlinkPayload};
use netlink_packet_netfilter::{
constants::*,
nflog::{
config_request,
nlas::{
Expand All @@ -19,7 +18,7 @@ use netlink_packet_netfilter::{
},
NfLogMessage,
},
NetfilterMessage, NetfilterMessageInner,
NetfilterMessage, NetfilterMessageInner, ProtoFamily,
};
use netlink_sys::{constants::NETLINK_NETFILTER, Socket};

Expand All @@ -43,7 +42,11 @@ fn main() {
socket.bind_auto().unwrap();

// Then we issue the PfBind command
let packet = config_request(AF_INET, 0, vec![ConfigCmd::PfBind.into()]);
let packet = config_request(
ProtoFamily::ProtoIPv4,
0,
vec![ConfigCmd::PfBind.into()],
);
let mut buf = vec![0; packet.header.length as usize];
packet.serialize(&mut buf[..]);
println!(">>> {:?}", packet);
Expand All @@ -64,7 +67,7 @@ fn main() {
// also set various parameters at the same time
let timeout: Timeout = Duration::from_millis(100).into();
let packet = config_request(
AF_INET,
ProtoFamily::ProtoIPv4,
1,
vec![
ConfigCmd::Bind.into(),
Expand Down
17 changes: 11 additions & 6 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: MIT

use crate::{
conntrack::ConntrackMessage,
message::{
NetfilterHeader, NetfilterMessage, NetfilterMessageInner,
NetfilterHeader, NetfilterMessage, NetfilterMessageInner, Subsystem,
NETFILTER_HEADER_LEN,
},
nflog::NfLogMessage,
Expand Down Expand Up @@ -52,15 +53,19 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
.context("failed to parse netfilter header")?;
let subsys = (message_type >> 8) as u8;
let message_type = message_type as u8;
let inner = match subsys {
NfLogMessage::SUBSYS => NetfilterMessageInner::NfLog(
let inner = match Subsystem::from(subsys) {
Subsystem::NfLog => NetfilterMessageInner::NfLog(
NfLogMessage::parse_with_param(buf, message_type)
.context("failed to parse nflog payload")?,
),
_ => NetfilterMessageInner::Other {
subsys,
Subsystem::Conntrack => NetfilterMessageInner::Conntrack(
ConntrackMessage::parse_with_param(buf, message_type)
.context("failed to parse conntrack payload")?,
),
subsys_enum @ Subsystem::Other(_) => NetfilterMessageInner::Other {
subsys: subsys_enum,
message_type,
nlas: buf.default_nlas()?,
attributes: buf.default_nlas()?,
},
};
Ok(NetfilterMessage::new(header, inner))
Expand Down
151 changes: 151 additions & 0 deletions src/conntrack/attributes/attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: MIT

use netlink_packet_core::{
emit_u32_be, parse_u32_be, DecodeError, DefaultNla, Emitable, ErrorContext,
Nla, NlaBuffer, NlasIterator, Parseable,
};

use crate::conntrack::attributes::{
protoinfo::ProtoInfo, status::Status, tuple::Tuple,
};

const CTA_TUPLE_ORIG: u16 = 1;
const CTA_TUPLE_REPLY: u16 = 2;
const CTA_PROTOINFO: u16 = 4;
const CTA_STATUS: u16 = 3;
const CTA_TIMEOUT: u16 = 7;
const CTA_MARK: u16 = 8;

#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConntrackNla {
CtaTupleOrig(Vec<Tuple>),
CtaTupleReply(Vec<Tuple>),
CtaProtoInfo(Vec<ProtoInfo>),
CtaStatus(Status),
CtaTimeout(u32),
CtaMark(u32),
Other(DefaultNla),
}

impl Nla for ConntrackNla {
fn value_len(&self) -> usize {
match self {
ConntrackNla::CtaTupleOrig(attr) => {
attr.iter().map(|op| op.buffer_len()).sum()
}
ConntrackNla::CtaTupleReply(attr) => {
attr.iter().map(|op| op.buffer_len()).sum()
}
ConntrackNla::CtaProtoInfo(attr) => {
attr.iter().map(|op| op.buffer_len()).sum()
}
ConntrackNla::CtaStatus(_) => size_of::<u32>(),
ConntrackNla::CtaTimeout(attr) => size_of_val(attr),
ConntrackNla::CtaMark(attr) => size_of_val(attr),
ConntrackNla::Other(attr) => attr.value_len(),
}
}

fn kind(&self) -> u16 {
match self {
ConntrackNla::CtaTupleOrig(_) => CTA_TUPLE_ORIG,
ConntrackNla::CtaTupleReply(_) => CTA_TUPLE_REPLY,
ConntrackNla::CtaProtoInfo(_) => CTA_PROTOINFO,
ConntrackNla::CtaStatus(_) => CTA_STATUS,
ConntrackNla::CtaTimeout(_) => CTA_TIMEOUT,
ConntrackNla::CtaMark(_) => CTA_MARK,
ConntrackNla::Other(attr) => attr.kind(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
ConntrackNla::CtaTupleOrig(attr) => {
let mut len = 0;
for op in attr {
op.emit(&mut buffer[len..]);
len += op.buffer_len();
}
}
ConntrackNla::CtaTupleReply(attr) => {
let mut len = 0;
for op in attr {
op.emit(&mut buffer[len..]);
len += op.buffer_len();
}
}
ConntrackNla::CtaProtoInfo(attr) => {
let mut len = 0;
for op in attr {
op.emit(&mut buffer[len..]);
len += op.buffer_len();
}
}
ConntrackNla::CtaStatus(attr) => {
emit_u32_be(buffer, (*attr).bits()).unwrap()
}
ConntrackNla::CtaTimeout(attr) => {
emit_u32_be(buffer, *attr).unwrap()
}
ConntrackNla::CtaMark(attr) => emit_u32_be(buffer, *attr).unwrap(),
ConntrackNla::Other(attr) => attr.emit_value(buffer),
}
}
fn is_nested(&self) -> bool {
matches!(
self,
ConntrackNla::CtaTupleOrig(_)
| ConntrackNla::CtaTupleReply(_)
| ConntrackNla::CtaProtoInfo(_)
)
}
}

impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
for ConntrackNla
{
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
let kind = buf.kind();
let payload = buf.value();
let nla = match kind {
CTA_TUPLE_ORIG => {
let mut tuples = Vec::new();
for nlas in NlasIterator::new(payload) {
let nlas = &nlas.context("invalid CTA_TUPLE_ORIG value")?;
tuples.push(Tuple::parse(nlas)?);
}
ConntrackNla::CtaTupleOrig(tuples)
}
CTA_TUPLE_REPLY => {
let mut tuples = Vec::new();
for nlas in NlasIterator::new(payload) {
let nlas =
&nlas.context("invalid CTA_TUPLE_REPLY value")?;

tuples.push(Tuple::parse(nlas)?);
}
ConntrackNla::CtaTupleReply(tuples)
}
CTA_PROTOINFO => {
let mut proto_infos = Vec::new();
for nlas in NlasIterator::new(payload) {
let nlas = &nlas.context("invalid CTA_PROTOINFO value")?;
proto_infos.push(ProtoInfo::parse(nlas)?);
}
ConntrackNla::CtaProtoInfo(proto_infos)
}
CTA_STATUS => ConntrackNla::CtaStatus(Status::from_bits_retain(
parse_u32_be(payload).context("invalid CTA_STATUS value")?,
)),
CTA_TIMEOUT => ConntrackNla::CtaTimeout(
parse_u32_be(payload).context("invalid CTA_TIMEOUT value")?,
),
CTA_MARK => ConntrackNla::CtaMark(
parse_u32_be(payload).context("invalid CTA_MARK value")?,
),
_ => ConntrackNla::Other(DefaultNla::parse(buf)?),
};
Ok(nla)
}
}
91 changes: 91 additions & 0 deletions src/conntrack/attributes/iptuple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT

use netlink_packet_core::{
parse_ip, DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable,
};
use std::net::IpAddr;

const CTA_IP_V4_SRC: u16 = 1;
const CTA_IP_V6_SRC: u16 = 3;
const CTA_IP_V4_DST: u16 = 2;
const CTA_IP_V6_DST: u16 = 4;

#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum IPTuple {
SourceAddress(IpAddr),
DestinationAddress(IpAddr),
Other(DefaultNla),
}

const IPV4_LEN: usize = 4;
const IPV6_LEN: usize = 16;

// Helper function needed for implementing the Nla trait
pub fn emit_ip(addr: &IpAddr, buf: &mut [u8]) {
match addr {
IpAddr::V4(ip) => {
buf[..IPV4_LEN].copy_from_slice(ip.octets().as_slice());
}
IpAddr::V6(ip) => {
buf[..IPV6_LEN].copy_from_slice(ip.octets().as_slice());
}
}
}

impl Nla for IPTuple {
fn value_len(&self) -> usize {
match self {
IPTuple::SourceAddress(attr) => match *attr {
IpAddr::V4(_) => IPV4_LEN,
IpAddr::V6(_) => IPV6_LEN,
},
IPTuple::DestinationAddress(attr) => match *attr {
IpAddr::V4(_) => IPV4_LEN,
IpAddr::V6(_) => IPV6_LEN,
},
IPTuple::Other(attr) => attr.value_len(),
}
}

fn kind(&self) -> u16 {
match self {
IPTuple::SourceAddress(attr) => match *attr {
IpAddr::V4(_) => CTA_IP_V4_SRC,
IpAddr::V6(_) => CTA_IP_V6_SRC,
},
IPTuple::DestinationAddress(attr) => match *attr {
IpAddr::V4(_) => CTA_IP_V4_DST,
IpAddr::V6(_) => CTA_IP_V6_DST,
},
IPTuple::Other(attr) => attr.kind(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
IPTuple::SourceAddress(attr) => emit_ip(attr, buffer),
IPTuple::DestinationAddress(attr) => emit_ip(attr, buffer),
IPTuple::Other(attr) => attr.emit_value(buffer),
}
}
}
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
for IPTuple
{
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
let kind = buf.kind();
let payload = buf.value();
let nla = match kind {
CTA_IP_V4_SRC | CTA_IP_V6_SRC => Self::SourceAddress(
parse_ip(payload).context("invalid SourceAddress value")?,
),
CTA_IP_V4_DST | CTA_IP_V6_DST => Self::DestinationAddress(
parse_ip(payload)
.context("invalid DestinationAddress value")?,
),
_ => IPTuple::Other(DefaultNla::parse(buf)?),
};
Ok(nla)
}
}
19 changes: 19 additions & 0 deletions src/conntrack/attributes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT

mod attribute;
mod iptuple;
mod protoinfo;
mod protoinfotcp;
mod prototuple;
mod status;
mod tcp_flags;
mod tuple;

pub use attribute::ConntrackNla;
pub use iptuple::IPTuple;
pub use protoinfo::ProtoInfo;
pub use protoinfotcp::ProtoInfoTCP;
pub use prototuple::{ProtoTuple, Protocol};
pub use status::Status;
pub use tcp_flags::TCPFlags;
pub use tuple::Tuple;
Loading