Skip to content

Commit 61dbec7

Browse files
committed
feat: conntrack get netlink netfilter message types
Implemented the following attributes 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 61dbec7

File tree

15 files changed

+916
-8
lines changed

15 files changed

+916
-8
lines changed

src/buffer.rs

Lines changed: 6 additions & 1 deletion
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,10 +58,14 @@ 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,
63-
nlas: buf.default_nlas()?,
68+
attributes: buf.default_nlas()?,
6469
},
6570
};
6671
Ok(NetfilterMessage::new(header, inner))
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::attributes::{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+
}
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/attributes/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
mod attribute;
4+
mod iptuple;
5+
mod protoinfo;
6+
mod protoinfotcp;
7+
mod prototuple;
8+
mod tcp_flags;
9+
mod tuple;
10+
11+
pub use attribute::ConntrackNla;
12+
pub use iptuple::IPTuple;
13+
pub use protoinfo::ProtoInfo;
14+
pub use protoinfotcp::ProtoInfoTCP;
15+
pub use prototuple::{ProtoTuple, Protocol};
16+
pub use tcp_flags::TCPFlags;
17+
pub use tuple::Tuple;
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::attributes::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+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_core::{
4+
parse_u8, DecodeError, DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer,
5+
Parseable,
6+
};
7+
8+
use crate::conntrack::attributes::tcp_flags::{TCPFlags, TCPFlagsBuffer};
9+
10+
const CTA_PROTOINFO_TCP_STATE: u16 = 1;
11+
const CTA_PROTOINFO_TCP_WSCALE_ORIGINAL: u16 = 2;
12+
const CTA_PROTOINFO_TCP_WSCALE_REPLY: u16 = 3;
13+
const CTA_PROTOINFO_TCP_FLAGS_ORIGINAL: u16 = 4;
14+
const CTA_PROTOINFO_TCP_FLAGS_REPLY: u16 = 5;
15+
16+
#[derive(Clone, Debug, PartialEq, Eq)]
17+
#[non_exhaustive]
18+
pub enum ProtoInfoTCP {
19+
State(u8),
20+
OriginalWindowScale(u8),
21+
ReplyWindowScale(u8),
22+
OriginalFlags(TCPFlags),
23+
ReplyFlags(TCPFlags),
24+
Other(DefaultNla),
25+
}
26+
impl Nla for ProtoInfoTCP {
27+
fn value_len(&self) -> usize {
28+
match self {
29+
ProtoInfoTCP::State(attr) => size_of_val(attr),
30+
ProtoInfoTCP::OriginalWindowScale(attr) => size_of_val(attr),
31+
ProtoInfoTCP::ReplyWindowScale(attr) => size_of_val(attr),
32+
ProtoInfoTCP::OriginalFlags(attr) => attr.buffer_len(),
33+
ProtoInfoTCP::ReplyFlags(attr) => attr.buffer_len(),
34+
ProtoInfoTCP::Other(attr) => attr.value_len(),
35+
}
36+
}
37+
38+
fn kind(&self) -> u16 {
39+
match self {
40+
ProtoInfoTCP::State(_) => CTA_PROTOINFO_TCP_STATE,
41+
ProtoInfoTCP::OriginalWindowScale(_) => {
42+
CTA_PROTOINFO_TCP_WSCALE_ORIGINAL
43+
}
44+
ProtoInfoTCP::ReplyWindowScale(_) => CTA_PROTOINFO_TCP_WSCALE_REPLY,
45+
ProtoInfoTCP::OriginalFlags(_) => CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
46+
ProtoInfoTCP::ReplyFlags(_) => CTA_PROTOINFO_TCP_FLAGS_REPLY,
47+
ProtoInfoTCP::Other(attr) => attr.kind(),
48+
}
49+
}
50+
51+
fn emit_value(&self, buffer: &mut [u8]) {
52+
match self {
53+
ProtoInfoTCP::State(attr) => buffer[0] = *attr,
54+
ProtoInfoTCP::OriginalWindowScale(attr) => buffer[0] = *attr,
55+
ProtoInfoTCP::ReplyWindowScale(attr) => buffer[0] = *attr,
56+
ProtoInfoTCP::OriginalFlags(attr) => attr.emit(buffer),
57+
ProtoInfoTCP::ReplyFlags(attr) => attr.emit(buffer),
58+
ProtoInfoTCP::Other(attr) => attr.emit_value(buffer),
59+
}
60+
}
61+
}
62+
impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'buffer T>>
63+
for ProtoInfoTCP
64+
{
65+
fn parse(buf: &NlaBuffer<&'buffer T>) -> Result<Self, DecodeError> {
66+
let kind = buf.kind();
67+
let payload = buf.value();
68+
let nla = match kind {
69+
CTA_PROTOINFO_TCP_STATE => ProtoInfoTCP::State(
70+
parse_u8(payload)
71+
.context("invalid CTA_PROTOINFO_TCP_STATE value")?,
72+
),
73+
CTA_PROTOINFO_TCP_WSCALE_ORIGINAL => {
74+
ProtoInfoTCP::OriginalWindowScale(parse_u8(payload).context(
75+
"invalid CTA_PROTOINFO_TCP_WSCALE_ORIGINAL value",
76+
)?)
77+
}
78+
CTA_PROTOINFO_TCP_WSCALE_REPLY => ProtoInfoTCP::ReplyWindowScale(
79+
parse_u8(payload)
80+
.context("invalid CTA_PROTOINFO_TCP_WSCALE_REPLY value")?,
81+
),
82+
CTA_PROTOINFO_TCP_FLAGS_ORIGINAL => ProtoInfoTCP::OriginalFlags(
83+
TCPFlags::parse(&TCPFlagsBuffer::new(payload)).context(
84+
"invalid CTA_PROTOINFO_TCP_FLAGS_ORIGINAL value",
85+
)?,
86+
),
87+
CTA_PROTOINFO_TCP_FLAGS_REPLY => ProtoInfoTCP::ReplyFlags(
88+
TCPFlags::parse(&TCPFlagsBuffer::new(payload))
89+
.context("invalid CTA_PROTOINFO_TCP_FLAGS_REPLY value")?,
90+
),
91+
_ => ProtoInfoTCP::Other(DefaultNla::parse(buf)?),
92+
};
93+
Ok(nla)
94+
}
95+
}

0 commit comments

Comments
 (0)