Skip to content
Merged
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
4 changes: 2 additions & 2 deletions docs/events/ct.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ for the bitset representing the corresponding values.

## Connection information

This starts by a protocol specific part. For TCP and UDP,
This starts by a protocol specific part. For TCP, UDP and SCTP,

```none
{protocol name} ({TCP state if any}) orig [{src ip}.{src port} > {dst ip}.{dst port}]
{protocol name} ({proto state if any}) orig [{src ip}.{src port} > {dst ip}.{dst port}]
reply [{src ip}.{src port} > {dst ip}.{dst port}] mark {mark} labels {labels}
```

Expand Down
2 changes: 2 additions & 0 deletions retis-events/src/compat/compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const FIXUPS: &[&[CompatFixup]] = &[
CompatValue::String("unknown"),
),
Add("startup/cmdline", CompatValue::String("unknown")),
Move("ct/tcp_state", "ct/proto_state"),
Move("ct/parent/tcp_state", "ct/parent/proto_state"),
],
];

Expand Down
35 changes: 32 additions & 3 deletions retis-events/src/ct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ pub struct CtIcmp {
pub id: u16,
}

/// SCTP
#[event_type]
#[derive(Default)]
pub struct CtSctp {
/// Source port.
pub sport: u16,
/// Destination port.
pub dport: u16,
}

#[event_type]
#[serde(rename_all = "snake_case")]
pub enum CtProto {
Expand All @@ -60,6 +70,10 @@ pub enum CtProto {
#[serde(flatten)]
icmp: CtIcmp,
},
Sctp {
#[serde(flatten)]
sctp: CtSctp,
},
}
impl Default for CtProto {
fn default() -> Self {
Expand Down Expand Up @@ -137,8 +151,8 @@ pub struct CtConnEvent {
pub orig: CtTuple,
/// Reply tuple.
pub reply: CtTuple,
/// TCP state.
pub tcp_state: Option<String>,
/// Protocol state.
pub proto_state: Option<String>,
/// Mark.
pub mark: Option<u32>,
/// Labels.
Expand Down Expand Up @@ -184,7 +198,7 @@ impl CtEvent {
write!(
f,
"tcp ({}) orig [{}.{} > {}.{}] reply [{}.{} > {}.{}] ",
conn.tcp_state.as_ref().unwrap_or(&"UNKNOWN".to_string()),
conn.proto_state.as_ref().unwrap_or(&"UNKNOWN".to_string()),
conn.orig.ip.src,
tcp_orig.sport,
conn.orig.ip.dst,
Expand Down Expand Up @@ -223,6 +237,21 @@ impl CtEvent {
icmp_reply.id,
)?;
}
(CtProto::Sctp { sctp: sctp_orig }, CtProto::Sctp { sctp: sctp_reply }) => {
write!(
f,
"sctp ({}) orig [{}.{} > {}.{}] reply [{}.{} > {}.{}] ",
conn.proto_state.as_ref().unwrap_or(&"UNKNOWN".to_string()),
conn.orig.ip.src,
sctp_orig.sport,
conn.orig.ip.dst,
sctp_orig.dport,
conn.reply.ip.src,
sctp_reply.sport,
conn.reply.ip.dst,
sctp_reply.dport,
)?;
}
_ => (),
}
match conn.zone_dir {
Expand Down
3 changes: 2 additions & 1 deletion retis/src/bindings/ct_hook_uapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const RETIS_CT_IPV6: ct_flags = 8;
pub const RETIS_CT_PROTO_TCP: ct_flags = 16;
pub const RETIS_CT_PROTO_UDP: ct_flags = 32;
pub const RETIS_CT_PROTO_ICMP: ct_flags = 64;
pub const RETIS_CT_PROTO_SCTP: ct_flags = 128;
pub type ct_flags = ::std::os::raw::c_uint;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
Expand Down Expand Up @@ -80,7 +81,7 @@ pub struct ct_event {
pub mark: u32_,
pub labels: [u8_; 16usize],
pub zone_id: u16_,
pub tcp_state: u8_,
pub proto_state: u8_,
}
impl Default for ct_event {
fn default() -> Self {
Expand Down
75 changes: 36 additions & 39 deletions retis/src/collect/collector/ct/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
use std::collections::HashMap;

use anyhow::{anyhow, bail, Result};
use btf_rs::Type;
use std::net::Ipv6Addr;

use crate::{
Expand All @@ -13,7 +12,7 @@ use crate::{
parse_raw_section, BpfRawSection, EventSectionFactory, FactoryId,
RawEventSectionFactory,
},
inspect::inspector,
inspect::{inspector, parse_enum},
},
event_section_factory,
events::{helpers::types::U128, *},
Expand All @@ -25,7 +24,7 @@ use crate::{
pub(crate) struct CtEventFactory {
mark_available: bool,
labels_available: bool,
tcp_states: HashMap<i32, String>,
proto_states: HashMap<u32, HashMap<u32, String>>,
}

impl RawEventSectionFactory for CtEventFactory {
Expand Down Expand Up @@ -93,36 +92,17 @@ impl CtEventFactory {
..Default::default()
};

me.parse_tcp_states()?;
me.proto_states.insert(
RETIS_CT_PROTO_TCP,
parse_enum("tcp_conntrack", &["TCP_CONNTRACK_"])?,
);
me.proto_states.insert(
RETIS_CT_PROTO_SCTP,
parse_enum("sctp_conntrack", &["SCTP_CONNTRACK_"])?,
);
Ok(me)
}

fn parse_tcp_states(&mut self) -> Result<()> {
if let Ok(types) = inspector()?
.kernel
.btf
.resolve_types_by_name("tcp_conntrack")
{
if let Some((btf, Type::Enum(r#enum))) =
types.iter().find(|(_, t)| matches!(t, Type::Enum(_)))
{
for member in r#enum.members.iter() {
if (member.val() as i32) < 0 {
continue;
}
self.tcp_states.insert(
member.val() as i32,
btf.resolve_name(member)?
.trim_start_matches("TCP_CONNTRACK_")
.to_string(),
);
}
}
}

Ok(())
}

pub(super) fn unmarshal_ct(&mut self, raw_section: &BpfRawSection) -> Result<CtConnEvent> {
let raw = parse_raw_section::<ct_event>(raw_section)?;
let flags = raw.flags;
Expand Down Expand Up @@ -221,18 +201,35 @@ impl CtEventFactory {
},
},
)
} else if flags & RETIS_CT_PROTO_SCTP != 0 {
(
CtProto::Sctp {
sctp: CtSctp {
sport: u16::from_be(raw.orig.src.data),
dport: u16::from_be(raw.orig.dst.data),
},
},
CtProto::Sctp {
sctp: CtSctp {
sport: u16::from_be(raw.reply.src.data),
dport: u16::from_be(raw.reply.dst.data),
},
},
)
} else {
bail!("ct: invalid protocol tuple information");
};

let tcp_state = if flags & RETIS_CT_PROTO_TCP != 0 {
match self.tcp_states.get(&(raw.tcp_state as i32)) {
Some(r) => Some(r.clone()),
None => Some(format!("{}", raw.tcp_state)),
}
} else {
None
};
let proto_state = [RETIS_CT_PROTO_TCP, RETIS_CT_PROTO_SCTP]
.iter()
.find(|&f| flags & f != 0)
.map(|f| {
self.proto_states
.get(f)
.and_then(|m| m.get(&(raw.proto_state as u32)))
.cloned()
.unwrap_or_else(|| format!("{}", raw.proto_state))
});

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

Expand All @@ -248,7 +245,7 @@ impl CtEventFactory {
ip: reply_ip,
proto: reply_proto,
},
tcp_state,
proto_state,
mark: if self.mark_available {
Some(raw.mark)
} else {
Expand Down
14 changes: 12 additions & 2 deletions retis/src/collect/collector/ct/bpf/ct_hook.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum ct_flags {
RETIS_CT_PROTO_TCP = 1 << 4,
RETIS_CT_PROTO_UDP = 1 << 5,
RETIS_CT_PROTO_ICMP = 1 << 6,
RETIS_CT_PROTO_SCTP = 1 << 7,
} __binding;

struct ct_meta_event {
Expand Down Expand Up @@ -63,7 +64,7 @@ struct ct_event {
u32 mark;
u8 labels[16];
u16 zone_id;
u8 tcp_state;
u8 proto_state;
} __binding;

static __always_inline bool ct_protocol_is_supported(u16 l3num, u8 protonum)
Expand All @@ -80,6 +81,7 @@ static __always_inline bool ct_protocol_is_supported(u16 l3num, u8 protonum)
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_ICMP:
case IPPROTO_SCTP:
break;
default:
return false;
Expand Down Expand Up @@ -181,7 +183,7 @@ static __always_inline int process_nf_conn(struct ct_event *e,
bpf_core_read(&e->reply.dst.data, sizeof(e->reply.dst.data),
&ct->REPLY.dst.u.tcp.port);

e->tcp_state = (u8)BPF_CORE_READ(ct, proto.tcp.state);
e->proto_state = (u8)BPF_CORE_READ(ct, proto.tcp.state);

break;
case IPPROTO_UDP:
Expand Down Expand Up @@ -212,6 +214,14 @@ static __always_inline int process_nf_conn(struct ct_event *e,
((u8) BPF_CORE_READ(ct, REPLY.dst.u.icmp.type) << 8) |
(u8) BPF_CORE_READ(ct, REPLY.dst.u.icmp.code);
break;
case IPPROTO_SCTP:
e->flags |= RETIS_CT_PROTO_SCTP;
e->orig.src.data = BPF_CORE_READ(ct, ORIG.src.u.sctp.port);
e->orig.dst.data = BPF_CORE_READ(ct, ORIG.dst.u.sctp.port);
e->reply.src.data = BPF_CORE_READ(ct, REPLY.src.u.sctp.port);
e->reply.dst.data = BPF_CORE_READ(ct, REPLY.dst.u.sctp.port);
e->proto_state = (u8)BPF_CORE_READ(ct, proto.sctp.state);
break;
}

get_nf_ct_labels(e, ct);
Expand Down