diff --git a/.github/workflows/core-build-checks.yml b/.github/workflows/core-build-checks.yml index fa5588a0..a738e619 100644 --- a/.github/workflows/core-build-checks.yml +++ b/.github/workflows/core-build-checks.yml @@ -2,11 +2,55 @@ name: Core Build Checks on: pull_request: - branches: [ main, PreReleaseMain ] + branches: [ main, PreReleaseMain, 0.1.5 ] push: branches: [ main, PreReleaseMain ] jobs: + check-common-features: + name: Check common features (${{ matrix.name }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: no features + args: --no-default-features + - name: network structs + args: --no-default-features --features network-structs + - name: monitoring structs + args: --no-default-features --features monitoring-structs + - name: network buffer reader + args: --no-default-features --features buffer-reader,network-structs + - name: monitoring buffer reader + args: --no-default-features --features buffer-reader,monitoring-structs + - name: handlers + args: --no-default-features --features map-handlers,program-handlers + - name: runtime features + args: --no-default-features --features map-handlers,program-handlers,network-structs,monitoring-structs,buffer-reader + - name: all features + args: --all-features + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check cortexbrain-common feature isolation + working-directory: core + run: cargo check --locked -p cortexbrain-common ${{ matrix.args }} + + test-common-contracts: + name: Test common event contracts + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Test cortexbrain-common public contracts + working-directory: core + run: cargo test --locked -p cortexbrain-common --test public_contracts --no-default-features --features network-structs,monitoring-structs + build-core-components: runs-on: ubuntu-latest diff --git a/core/common/src/buffer_type.rs b/core/common/src/buffer_type.rs index 2a0f1f06..e700d833 100644 --- a/core/common/src/buffer_type.rs +++ b/core/common/src/buffer_type.rs @@ -198,6 +198,7 @@ pub struct SslEvent { pub size: i32, // return value (bytes transferred or <0 on error) pub requested: i32, // num argument passed to SSL_read/SSL_write } +#[cfg(feature = "monitoring-structs")] unsafe impl aya::Pod for SslEvent {} /// Perform a byte swap from little-endian to big-endian. diff --git a/core/common/src/consumer.rs b/core/common/src/consumer.rs index ef082bc6..16d0ab5d 100644 --- a/core/common/src/consumer.rs +++ b/core/common/src/consumer.rs @@ -639,6 +639,7 @@ impl Consumer { } } + #[cfg(feature = "monitoring-structs")] pub async fn read_ssl_events( buffers: &mut [BytesMut], tot_events: i32, diff --git a/core/common/tests/public_contracts.rs b/core/common/tests/public_contracts.rs new file mode 100644 index 00000000..9afc3505 --- /dev/null +++ b/core/common/tests/public_contracts.rs @@ -0,0 +1,162 @@ +use cortexbrain_common::formatters::{format_ipv4, format_ipv6}; + +#[test] +fn address_formatters_preserve_network_byte_order() { + let ipv4 = u32::from_ne_bytes([192, 0, 2, 1]); + assert_eq!(format_ipv4(ipv4), "192.0.2.1"); + assert_eq!(format_ipv4(0), "0.0.0.0"); + + assert_eq!(format_ipv6(&[0x2001_0db8, 0, 0, 1]), "2001:db8:0:0:0:0:0:1"); + assert_eq!(format_ipv6(&[0; 4]), "0:0:0:0:0:0:0:0"); +} + +#[cfg(any(feature = "network-structs", feature = "monitoring-structs"))] +mod buffer_contracts { + use cortexbrain_common::buffer_type::{IpProtocols, reverse_be_addr}; + + #[test] + fn protocol_numbers_follow_the_ipv4_header_contract() { + assert!(matches!(IpProtocols::try_from(1), Ok(IpProtocols::ICMP))); + assert!(matches!(IpProtocols::try_from(6), Ok(IpProtocols::TCP))); + assert!(matches!(IpProtocols::try_from(17), Ok(IpProtocols::UDP))); + + for unknown in [0, 2, 255] { + assert!(IpProtocols::try_from(unknown).is_err()); + } + } + + #[test] + fn raw_ipv4_addresses_are_reversed_by_octet() { + let raw_address = u32::from_le_bytes([192, 0, 2, 1]); + assert_eq!(reverse_be_addr(raw_address).octets(), [192, 0, 2, 1]); + assert_eq!(reverse_be_addr(0).octets(), [0, 0, 0, 0]); + assert_eq!(reverse_be_addr(u32::MAX).octets(), [255, 255, 255, 255]); + } +} + +#[cfg(feature = "network-structs")] +mod network_event_contracts { + use cortexbrain_common::buffer_type::{PacketLog, TcpPacketRegistry, VethLog}; + use std::mem::{align_of, offset_of, size_of}; + + fn assert_event_traits() {} + + #[test] + fn network_events_are_pod_compatible() { + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + } + + #[test] + fn network_event_layouts_match_the_ebpf_side() { + assert_eq!(size_of::(), 24); + assert_eq!(align_of::(), 4); + assert_eq!(offset_of!(PacketLog, proto), 0); + assert_eq!(offset_of!(PacketLog, src_ip), 4); + assert_eq!(offset_of!(PacketLog, src_port), 8); + assert_eq!(offset_of!(PacketLog, dst_ip), 12); + assert_eq!(offset_of!(PacketLog, dst_port), 16); + assert_eq!(offset_of!(PacketLog, pid), 20); + + assert_eq!(size_of::(), 39); + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(VethLog, name), 0); + assert_eq!(offset_of!(VethLog, state), 16); + assert_eq!(offset_of!(VethLog, dev_addr), 24); + assert_eq!(offset_of!(VethLog, event_type), 30); + assert_eq!(offset_of!(VethLog, netns), 31); + assert_eq!(offset_of!(VethLog, pid), 35); + + assert_eq!(size_of::(), 48); + assert_eq!(align_of::(), 8); + assert_eq!(offset_of!(TcpPacketRegistry, proto), 0); + assert_eq!(offset_of!(TcpPacketRegistry, src_ip), 4); + assert_eq!(offset_of!(TcpPacketRegistry, dst_ip), 8); + assert_eq!(offset_of!(TcpPacketRegistry, src_port), 12); + assert_eq!(offset_of!(TcpPacketRegistry, dst_port), 14); + assert_eq!(offset_of!(TcpPacketRegistry, pid), 16); + assert_eq!(offset_of!(TcpPacketRegistry, command), 20); + assert_eq!(offset_of!(TcpPacketRegistry, cgroup_id), 40); + } +} + +#[cfg(feature = "monitoring-structs")] +mod monitoring_event_contracts { + use cortexbrain_common::buffer_type::{ + CpuFrequency, CpuIdle, MemAlloc, PacketLossMetrics, SchedStatRuntime, SchedStatWait, + SslEvent, TASK_COMM_LEN, TimeStampMetrics, + }; + use std::mem::{align_of, offset_of, size_of}; + + fn assert_event_traits() {} + + #[test] + fn monitoring_events_are_pod_compatible() { + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + assert_event_traits::(); + } + + #[test] + fn monitoring_event_layouts_match_the_ebpf_side() { + assert_eq!(TASK_COMM_LEN, 16); + + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(PacketLossMetrics, tgid), 0); + assert_eq!(offset_of!(PacketLossMetrics, comm), 4); + assert_eq!(offset_of!(PacketLossMetrics, ts_us), 20); + assert_eq!(offset_of!(PacketLossMetrics, sk_err), 28); + assert_eq!(offset_of!(PacketLossMetrics, sk_drops), 52); + + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(TimeStampMetrics, delta_us), 0); + assert_eq!(offset_of!(TimeStampMetrics, ts_us), 8); + assert_eq!(offset_of!(TimeStampMetrics, tgid), 16); + assert_eq!(offset_of!(TimeStampMetrics, comm), 20); + assert_eq!(offset_of!(TimeStampMetrics, lport), 36); + assert_eq!(offset_of!(TimeStampMetrics, af), 40); + assert_eq!(offset_of!(TimeStampMetrics, saddr_v4), 42); + assert_eq!(offset_of!(TimeStampMetrics, daddr_v6), 66); + + assert_eq!(size_of::(), 24); + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(CpuFrequency, bytes_alloc), 0); + assert_eq!(offset_of!(CpuFrequency, pid), 4); + assert_eq!(offset_of!(CpuFrequency, command), 8); + + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(MemAlloc, tgid), 0); + assert_eq!(offset_of!(MemAlloc, length), 4); + assert_eq!(offset_of!(MemAlloc, addr), 12); + assert_eq!(offset_of!(MemAlloc, command), 20); + + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(SchedStatWait, tgid), 0); + assert_eq!(offset_of!(SchedStatWait, delay), 4); + assert_eq!(offset_of!(SchedStatWait, command), 12); + + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(SchedStatRuntime, tgid), 0); + assert_eq!(offset_of!(SchedStatRuntime, runtime), 4); + assert_eq!(offset_of!(SchedStatRuntime, command), 12); + + assert_eq!(size_of::(), 8); + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(CpuIdle, cpu_id), 0); + assert_eq!(offset_of!(CpuIdle, state), 4); + + assert_eq!(align_of::(), 1); + assert_eq!(offset_of!(SslEvent, tgid), 0); + assert_eq!(offset_of!(SslEvent, comm), 4); + assert_eq!(offset_of!(SslEvent, ts_us), 20); + assert_eq!(offset_of!(SslEvent, direction), 28); + assert_eq!(offset_of!(SslEvent, size), 29); + assert_eq!(offset_of!(SslEvent, requested), 33); + } +}