Skip to content

Commit

Permalink
module: ct: add support for retrieving conntrack labels
Browse files Browse the repository at this point in the history
While labels are an optional feature that can be compiled out, let's not
use a dedicated event section for just 128 bits. Instead we detect if
the labels value should be seen as valid in the Rust side while
processing the raw event. Also 0 means there is no label.

Signed-off-by: Antoine Tenart <[email protected]>
  • Loading branch information
atenart committed Oct 31, 2024
1 parent 72615dc commit 3f70aed
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 3 deletions.
8 changes: 7 additions & 1 deletion retis-events/src/ct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt;

use super::*;
use super::{helpers::U128, *};
use crate::{event_section, event_type, Formatter};

#[event_type]
Expand Down Expand Up @@ -127,6 +127,8 @@ pub struct CtConnEvent {
pub tcp_state: Option<String>,
/// Connection mark tracking label
pub mark: Option<u32>,
/// Connection tracking labels.
pub labels: Option<U128>,
}

impl EventFmt for CtEvent {
Expand Down Expand Up @@ -212,6 +214,10 @@ impl CtEvent {
write!(f, " mark {}", mark)?;
}

if let Some(labels) = &conn.labels {
write!(f, " labels {:#x}", labels.bits())?;
}

Ok(())
}
}
21 changes: 21 additions & 0 deletions retis-events/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,27 @@ pub(crate) fn protocol_str(protocol: u8) -> Option<&'static str> {
})
}

/// u128 representation in the events. We can't use the Rust primitive as serde
/// does not handle the type well.
#[event_type]
pub struct U128 {
hi: u64,
lo: u64,
}

impl U128 {
pub fn from_u128(from: u128) -> Self {
Self {
hi: (from >> 64) as u64,
lo: from as u64,
}
}

pub fn bits(&self) -> u128 {
(self.hi as u128) << 64 | self.lo as u128
}
}

/// Represents a raw packet. Stored internally as a `Vec<u8>`.
/// We don't use #[event_type] as we're implementing serde::Serialize and
/// serde::Deserialize manually.
Expand Down
1 change: 1 addition & 0 deletions retis/src/bindings/ct_uapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub struct ct_event {
pub reply: nf_conn_tuple,
pub flags: u32_,
pub mark: u32_,
pub labels: [u8_; 16usize],
pub zone_id: u16_,
pub tcp_state: u8_,
}
Expand Down
6 changes: 6 additions & 0 deletions retis/src/core/probe/kernel/bpf/include/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@

#define MIN(a, b) (((a) < (b)) ? (a) : (b))

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define BUILD_BUG_ON(cond) _Static_assert(!(cond), "BUILD_BUG_ON failed " #cond)
#else
#define BUILD_BUG_ON(cond)
#endif

enum bpf_func_id___x { BPF_FUNC_get_func_ip___5_15_0 = 42 };

/* The following helper retrieves the function IP in kprobes.
Expand Down
17 changes: 15 additions & 2 deletions retis/src/module/ct/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ use crate::{
inspect::inspector,
},
event_section_factory,
events::*,
events::{helpers::U128, *},
helpers,
};

#[event_section_factory(FactoryId::Ct)]
#[derive(Default)]
pub(crate) struct CtEventFactory {
mark_available: bool,
labels_available: bool,
tcp_states: HashMap<i32, String>,
}

Expand Down Expand Up @@ -73,12 +74,17 @@ impl RawEventSectionFactory for CtEventFactory {

impl CtEventFactory {
pub(super) fn new() -> Result<Self> {
let kconf_mark = inspector()?
let inspector = inspector()?;
let kconf_mark = inspector
.kernel
.get_config_option("CONFIG_NF_CONNTRACK_MARK")?;
let kconf_labels = inspector
.kernel
.get_config_option("CONFIG_NF_CONNTRACK_LABELS")?;

let mut me = Self {
mark_available: kconf_mark == Some("y"),
labels_available: kconf_labels == Some("y"),
..Default::default()
};

Expand Down Expand Up @@ -211,6 +217,8 @@ impl CtEventFactory {
None
};

let labels = U128::from_u128(u128::from_ne_bytes(raw.labels));

Ok(CtConnEvent {
zone_id: raw.zone_id,
zone_dir,
Expand All @@ -228,6 +236,11 @@ impl CtEventFactory {
} else {
None
},
labels: if self.labels_available && labels.bits() != 0 {
Some(labels)
} else {
None
},
})
}
}
Expand Down
31 changes: 31 additions & 0 deletions retis/src/module/ct/bpf/ct.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct ct_event {
struct nf_conn_tuple reply;
u32 flags;
u32 mark;
u8 labels[16];
u16 zone_id;
u8 tcp_state;
} __binding;
Expand All @@ -86,6 +87,33 @@ static __always_inline bool ct_protocol_is_supported(u16 l3num, u8 protonum)
return true;
}

/* See ctnetlink_dump_labels(). */
static __always_inline void get_nf_ct_labels(struct ct_event *e,
struct nf_conn *ct)
{
struct nf_ct_ext *ext = BPF_CORE_READ(ct, ext);
struct nf_conn_labels *labels;
int offset;

/* Conntrack labels depend on CONFIG_NF_CONNTRACK_LABELS, the following
* enum variant is only defined if enabled.
*/
if (!bpf_core_enum_value_exists(enum nf_ct_ext_id, NF_CT_EXT_LABELS))
return;

if (!ext)
return;

offset = BPF_CORE_READ(ext, offset)[NF_CT_EXT_LABELS];
if (!offset)
return;

labels = (void *)ext + offset;

BUILD_BUG_ON(sizeof(labels->bits) != sizeof(e->labels));
bpf_core_read(&e->labels, sizeof(labels->bits), &labels->bits);
}

static __always_inline int process_nf_conn(struct ct_event *e,
struct nf_conn *ct, u16 l3num,
u8 protonum)
Expand Down Expand Up @@ -183,6 +211,8 @@ static __always_inline int process_nf_conn(struct ct_event *e,
break;
}

get_nf_ct_labels(e, ct);

return 0;
}

Expand Down Expand Up @@ -231,6 +261,7 @@ DEFINE_HOOK(F_AND, RETIS_ALL_FILTERS,
process_nf_conn(e, nf_conn,
(u16)BPF_CORE_READ(nf_conn, ORIG.src.l3num),
(u8)BPF_CORE_READ(nf_conn, ORIG.dst.protonum));

}

m = get_event_section(event, COLLECTOR_CT, SECTION_META, sizeof(*m));
Expand Down

0 comments on commit 3f70aed

Please sign in to comment.