Skip to content

Commit eaaa88f

Browse files
committed
feat: conntrack get netlink netfilter message types
Implemented the following types required to successfully construct a conntrack get request: * iptuple * protoinfo * protoinfotcp * prototuple * tcp_flags * tuple Signed-off-by: Shivang K Raghuvanshi <[email protected]>
1 parent e8f786a commit eaaa88f

File tree

16 files changed

+819
-9
lines changed

16 files changed

+819
-9
lines changed

examples/nflog.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

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

11-
use byteorder::{ByteOrder, NetworkEndian};
12-
use netlink_packet_core::{NetlinkMessage, NetlinkPayload};
11+
use netlink_packet_core::{parse_u32_be, NetlinkMessage, NetlinkPayload};
1312
use netlink_packet_netfilter::{
1413
constants::*,
1514
nflog::{
@@ -103,12 +102,12 @@ fn main() {
103102

104103
for nla in get_packet_nlas(&rx_packet) {
105104
if let PacketNla::Payload(payload) = nla {
106-
let src = Ipv4Addr::from(NetworkEndian::read_u32(
107-
&payload[12..],
108-
));
109-
let dst = Ipv4Addr::from(NetworkEndian::read_u32(
110-
&payload[16..],
111-
));
105+
let src = Ipv4Addr::from(
106+
parse_u32_be(&payload[12..]).unwrap(),
107+
);
108+
let dst = Ipv4Addr::from(
109+
parse_u32_be(&payload[16..]).unwrap(),
110+
);
112111
println!("Packet from {} to {}", src, dst);
113112
break;
114113
}

src/buffer.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT
22

33
use crate::{
4+
conntrack::ConntrackMessage,
45
message::{
56
NetfilterHeader, NetfilterMessage, NetfilterMessageInner,
67
NETFILTER_HEADER_LEN,
@@ -57,6 +58,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
5758
NfLogMessage::parse_with_param(buf, message_type)
5859
.context("failed to parse nflog payload")?,
5960
),
61+
ConntrackMessage::SUBSYS => NetfilterMessageInner::Conntrack(
62+
ConntrackMessage::parse_with_param(buf, message_type)
63+
.context("failed to parse conntrack payload")?,
64+
),
6065
_ => NetfilterMessageInner::Other {
6166
subsys,
6267
message_type,

src/conntrack/message.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use crate::{
4+
buffer::NetfilterBuffer,
5+
conntrack::nlas::nla::ConntrackNla,
6+
constants::{IPCTNL_MSG_CT_GET, NFNL_SUBSYS_CTNETLINK},
7+
};
8+
use netlink_packet_core::{
9+
DecodeError, DefaultNla, Emitable, Parseable, ParseableParametrized,
10+
};
11+
12+
#[derive(Debug, PartialEq, Eq, Clone)]
13+
pub enum ConntrackMessage {
14+
Get(Vec<ConntrackNla>),
15+
Other {
16+
message_type: u8,
17+
nlas: Vec<DefaultNla>,
18+
},
19+
}
20+
21+
impl ConntrackMessage {
22+
pub const SUBSYS: u8 = NFNL_SUBSYS_CTNETLINK;
23+
24+
pub fn message_type(&self) -> u8 {
25+
match self {
26+
ConntrackMessage::Get(_) => IPCTNL_MSG_CT_GET,
27+
ConntrackMessage::Other { message_type, .. } => *message_type,
28+
}
29+
}
30+
}
31+
32+
impl Emitable for ConntrackMessage {
33+
fn buffer_len(&self) -> usize {
34+
match self {
35+
ConntrackMessage::Get(nlas) => nlas.as_slice().buffer_len(),
36+
ConntrackMessage::Other { nlas, .. } => {
37+
nlas.as_slice().buffer_len()
38+
}
39+
}
40+
}
41+
42+
fn emit(&self, buffer: &mut [u8]) {
43+
match self {
44+
ConntrackMessage::Get(nlas) => nlas.as_slice().emit(buffer),
45+
ConntrackMessage::Other { nlas, .. } => {
46+
nlas.as_slice().emit(buffer)
47+
}
48+
};
49+
}
50+
}
51+
52+
impl<'a, T: AsRef<[u8]> + ?Sized>
53+
ParseableParametrized<NetfilterBuffer<&'a T>, u8> for ConntrackMessage
54+
{
55+
fn parse_with_param(
56+
buf: &NetfilterBuffer<&'a T>,
57+
message_type: u8,
58+
) -> Result<Self, DecodeError> {
59+
Ok(match message_type {
60+
IPCTNL_MSG_CT_GET => {
61+
let nlas = buf
62+
.parse_all_nlas(|nla_buf| ConntrackNla::parse(&nla_buf))?;
63+
ConntrackMessage::Get(nlas)
64+
}
65+
_ => ConntrackMessage::Other {
66+
message_type,
67+
nlas: buf.default_nlas()?,
68+
},
69+
})
70+
}
71+
}

src/conntrack/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
mod message;
4+
pub use message::ConntrackMessage;
5+
pub mod nlas;

src/conntrack/nlas/iptuple.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use derive_more::{From, IsVariant};
4+
use netlink_packet_core::{
5+
parse_ip, DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable,
6+
};
7+
use std::net::IpAddr;
8+
9+
use crate::constants::{
10+
CTA_IP_V4_DST, CTA_IP_V4_SRC, CTA_IP_V6_DST, CTA_IP_V6_SRC,
11+
};
12+
13+
#[derive(Clone, Debug, PartialEq, Eq, From, IsVariant)]
14+
pub enum IPTuple {
15+
SourceAddress(IpAddr),
16+
#[from(ignore)]
17+
DestinationAddress(IpAddr),
18+
Other(DefaultNla),
19+
}
20+
21+
pub const IPV4_LEN: usize = 4;
22+
pub const IPV6_LEN: usize = 16;
23+
24+
// Helper function needed for implementing the Nla trait
25+
pub fn emit_ip(addr: &IpAddr, buf: &mut [u8]) {
26+
match addr {
27+
IpAddr::V4(ip) => {
28+
buf[..IPV4_LEN].copy_from_slice(ip.octets().as_slice());
29+
}
30+
IpAddr::V6(ip) => {
31+
buf[..IPV6_LEN].copy_from_slice(ip.octets().as_slice());
32+
}
33+
}
34+
}
35+
36+
impl Nla for IPTuple {
37+
fn value_len(&self) -> usize {
38+
match self {
39+
IPTuple::SourceAddress(attr) => match *attr {
40+
IpAddr::V4(_) => IPV4_LEN,
41+
IpAddr::V6(_) => IPV6_LEN,
42+
},
43+
IPTuple::DestinationAddress(attr) => match *attr {
44+
IpAddr::V4(_) => IPV4_LEN,
45+
IpAddr::V6(_) => IPV6_LEN,
46+
},
47+
IPTuple::Other(attr) => attr.value_len(),
48+
}
49+
}
50+
51+
fn kind(&self) -> u16 {
52+
match self {
53+
IPTuple::SourceAddress(attr) => match *attr {
54+
IpAddr::V4(_) => CTA_IP_V4_SRC,
55+
IpAddr::V6(_) => CTA_IP_V6_SRC,
56+
},
57+
IPTuple::DestinationAddress(attr) => match *attr {
58+
IpAddr::V4(_) => CTA_IP_V4_DST,
59+
IpAddr::V6(_) => CTA_IP_V6_DST,
60+
},
61+
IPTuple::Other(attr) => attr.kind(),
62+
}
63+
}
64+
65+
fn emit_value(&self, buffer: &mut [u8]) {
66+
match self {
67+
IPTuple::SourceAddress(attr) => emit_ip(attr, buffer),
68+
IPTuple::DestinationAddress(attr) => emit_ip(attr, buffer),
69+
IPTuple::Other(attr) => attr.emit_value(buffer),
70+
}
71+
}
72+
}
73+
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
74+
for IPTuple
75+
{
76+
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
77+
let kind = buf.kind();
78+
let payload = buf.value();
79+
let nla = match kind {
80+
CTA_IP_V4_SRC | CTA_IP_V6_SRC => Self::SourceAddress(
81+
parse_ip(payload).context("invalid SourceAddress value")?,
82+
),
83+
CTA_IP_V4_DST | CTA_IP_V6_DST => Self::DestinationAddress(
84+
parse_ip(payload)
85+
.context("invalid DestinationAddress value")?,
86+
),
87+
_ => IPTuple::Other(DefaultNla::parse(buf)?),
88+
};
89+
Ok(nla)
90+
}
91+
}

src/conntrack/nlas/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
mod iptuple;
4+
pub mod nla;
5+
mod protoinfo;
6+
mod protoinfotcp;
7+
mod prototuple;
8+
mod tcp_flags;
9+
mod tuple;
10+
11+
pub use iptuple::IPTuple;
12+
pub use protoinfo::ProtoInfo;
13+
pub use protoinfotcp::ProtoInfoTCP;
14+
pub use prototuple::ProtoTuple;
15+
pub use tcp_flags::TCPFlags;
16+
pub use tuple::Tuple;

src/conntrack/nlas/nla.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use derive_more::{From, IsVariant};
4+
use netlink_packet_core::{
5+
DecodeError, DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer,
6+
NlasIterator, Parseable,
7+
};
8+
9+
use crate::{
10+
conntrack::nlas::{protoinfo::ProtoInfo, tuple::Tuple},
11+
constants::{CTA_PROTOINFO, CTA_TUPLE_ORIG},
12+
};
13+
14+
#[derive(Clone, Debug, PartialEq, Eq, From, IsVariant)]
15+
pub enum ConntrackNla {
16+
CtaTupleOrig(Vec<Tuple>),
17+
CtaProtoInfo(Vec<ProtoInfo>),
18+
Other(DefaultNla),
19+
}
20+
21+
impl Nla for ConntrackNla {
22+
fn value_len(&self) -> usize {
23+
match self {
24+
ConntrackNla::CtaTupleOrig(attr) => {
25+
attr.iter().map(|op| op.buffer_len()).sum()
26+
}
27+
ConntrackNla::CtaProtoInfo(attr) => {
28+
attr.iter().map(|op| op.buffer_len()).sum()
29+
}
30+
ConntrackNla::Other(attr) => attr.value_len(),
31+
}
32+
}
33+
34+
fn kind(&self) -> u16 {
35+
match self {
36+
ConntrackNla::CtaTupleOrig(_) => CTA_TUPLE_ORIG,
37+
ConntrackNla::CtaProtoInfo(_) => CTA_PROTOINFO,
38+
ConntrackNla::Other(attr) => attr.kind(),
39+
}
40+
}
41+
42+
fn emit_value(&self, buffer: &mut [u8]) {
43+
match self {
44+
ConntrackNla::CtaTupleOrig(attr) => {
45+
let mut len = 0;
46+
for op in attr {
47+
op.emit(&mut buffer[len..]);
48+
len += op.buffer_len();
49+
}
50+
}
51+
ConntrackNla::CtaProtoInfo(attr) => {
52+
let mut len = 0;
53+
for op in attr {
54+
op.emit(&mut buffer[len..]);
55+
len += op.buffer_len();
56+
}
57+
}
58+
ConntrackNla::Other(attr) => attr.emit_value(buffer),
59+
}
60+
}
61+
fn is_nested(&self) -> bool {
62+
matches!(
63+
self,
64+
ConntrackNla::CtaTupleOrig(_) | ConntrackNla::CtaProtoInfo(_)
65+
)
66+
}
67+
}
68+
69+
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
70+
for ConntrackNla
71+
{
72+
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
73+
let kind = buf.kind();
74+
let payload = buf.value();
75+
let nla = match kind {
76+
CTA_TUPLE_ORIG => {
77+
let mut tuples = Vec::new();
78+
for nlas in NlasIterator::new(payload) {
79+
let nlas = &nlas.context("invalid CTA_TUPLE_ORIG value")?;
80+
tuples.push(Tuple::parse(nlas)?);
81+
}
82+
ConntrackNla::CtaTupleOrig(tuples)
83+
}
84+
CTA_PROTOINFO => {
85+
let mut proto_infos = Vec::new();
86+
for nlas in NlasIterator::new(payload) {
87+
let nlas = &nlas.context("invalid CTA_PROTOINFO value")?;
88+
proto_infos.push(ProtoInfo::parse(nlas)?);
89+
}
90+
ConntrackNla::CtaProtoInfo(proto_infos)
91+
}
92+
_ => ConntrackNla::Other(DefaultNla::parse(buf)?),
93+
};
94+
Ok(nla)
95+
}
96+
}

0 commit comments

Comments
 (0)