Skip to content

Commit 62cb27e

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 fdd02dd commit 62cb27e

File tree

15 files changed

+903
-2
lines changed

15 files changed

+903
-2
lines changed

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

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 netlink_packet_core::{
4+
parse_ip, DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable,
5+
};
6+
use std::net::IpAddr;
7+
8+
const CTA_IP_V4_SRC: u16 = 1;
9+
const CTA_IP_V6_SRC: u16 = 3;
10+
const CTA_IP_V4_DST: u16 = 2;
11+
const CTA_IP_V6_DST: u16 = 4;
12+
13+
#[derive(Clone, Debug, PartialEq, Eq)]
14+
#[non_exhaustive]
15+
pub enum IPTuple {
16+
SourceAddress(IpAddr),
17+
DestinationAddress(IpAddr),
18+
Other(DefaultNla),
19+
}
20+
21+
const IPV4_LEN: usize = 4;
22+
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, Protocol};
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 netlink_packet_core::{
4+
DecodeError, DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer,
5+
NlasIterator, Parseable,
6+
};
7+
8+
use crate::conntrack::nlas::{protoinfo::ProtoInfo, tuple::Tuple};
9+
10+
const CTA_TUPLE_ORIG: u16 = 1;
11+
const CTA_PROTOINFO: u16 = 4;
12+
13+
#[derive(Clone, Debug, PartialEq, Eq)]
14+
#[non_exhaustive]
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+
}

src/conntrack/nlas/protoinfo.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_core::{
4+
DecodeError, DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer,
5+
NlasIterator, Parseable,
6+
};
7+
8+
use crate::conntrack::nlas::protoinfotcp::ProtoInfoTCP;
9+
10+
const CTA_PROTOINFO_TCP: u16 = 1;
11+
12+
#[derive(Clone, Debug, PartialEq, Eq)]
13+
#[non_exhaustive]
14+
pub enum ProtoInfo {
15+
TCP(Vec<ProtoInfoTCP>),
16+
Other(DefaultNla),
17+
}
18+
impl Nla for ProtoInfo {
19+
fn value_len(&self) -> usize {
20+
match self {
21+
ProtoInfo::TCP(nlas) => nlas.iter().map(|op| op.buffer_len()).sum(),
22+
ProtoInfo::Other(attr) => attr.value_len(),
23+
}
24+
}
25+
26+
fn kind(&self) -> u16 {
27+
match self {
28+
ProtoInfo::TCP(_) => CTA_PROTOINFO_TCP,
29+
ProtoInfo::Other(attr) => attr.kind(),
30+
}
31+
}
32+
fn emit_value(&self, buffer: &mut [u8]) {
33+
match self {
34+
ProtoInfo::TCP(nlas) => {
35+
let mut len = 0;
36+
for op in nlas {
37+
op.emit(&mut buffer[len..]);
38+
len += op.buffer_len();
39+
}
40+
}
41+
ProtoInfo::Other(attr) => attr.emit_value(buffer),
42+
}
43+
}
44+
fn is_nested(&self) -> bool {
45+
matches!(self, ProtoInfo::TCP(_))
46+
}
47+
}
48+
49+
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
50+
for ProtoInfo
51+
{
52+
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
53+
let kind = buf.kind();
54+
let payload = buf.value();
55+
let nla = match kind {
56+
CTA_PROTOINFO_TCP => {
57+
let mut proto_info_tcps = Vec::new();
58+
for nlas in NlasIterator::new(payload) {
59+
let nlas =
60+
&nlas.context("invailid CTA_PROTOINFO_TCP value")?;
61+
proto_info_tcps.push(ProtoInfoTCP::parse(nlas)?);
62+
}
63+
ProtoInfo::TCP(proto_info_tcps)
64+
}
65+
_ => ProtoInfo::Other(DefaultNla::parse(buf)?),
66+
};
67+
Ok(nla)
68+
}
69+
}

0 commit comments

Comments
 (0)