BastionGuard/thirdparty/bastionguard-secure-connection/daemon/include/bsc/ebpf/EBPFMon.hpp
specialworld83 f0f913a209 Release 2.0
2026-07-15 10:52:22 +02:00

137 lines
4.7 KiB
C++

#pragma once
/*
* BastionGuard™ Secure Connection — EBPFMon
* Copyright (C) 2025–2026 Calogero Scarnà
* GPL-3.0
*
* Loader userspace per il programma eBPF bsc_ebpf.o.
* Apre il pinned BPF object (o lo carica dal skel generato da bpftool),
* fa attach di TUTTI i programmi presenti nell'oggetto (tracepoint +
* kprobes TCP connect + kprobes UDP sendmsg), e drena la ring buffer
* in un thread background.
*
* INTERFACCIA PUBBLICA:
* EBPFMon mon;
* if (mon.start()) { ... } // avvia draining thread
* auto events = mon.drain(); // consuma gli eventi accumulati
* mon.stop(); // join thread, detach bpf
*
* drain() e' lock-free dal punto di vista del chiamante: scambia
* atomicamente il buffer interno con uno vuoto — O(1) e senza copie.
*
* NOTA: se libbpf non e' disponibile a compile-time (BSC_HAS_EBPF non
* definito), tutte le implementazioni sono no-op e drain() ritorna
* sempre un vettore vuoto. Il resto del daemon non nota la differenza.
*/
#include "bsc/model/Connection.hpp"
#include <atomic>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
// Questa struttura DEVE essere identica a bsc_conn_event in bsc_ebpf.c
// Viene usata per interpretare i byte della ring buffer.
//
// NB: layout conservato rispetto alla v1:
// la v1 aveva uint8_t _pad[2]
// la v2 ha uint8_t event_kind + uint8_t _pad[1]
// Stessa dimensione totale, stessi offset → un loader v2 legge un
// oggetto v1 con event_kind=0 (valore di padding), retro-compat OK.
namespace bsc::ebpf {
enum EventKind : uint8_t {
EVT_TP_SET_STATE = 0, // tracepoint inet_sock_set_state
EVT_TCP_CONNECT = 1, // kprobe tcp_v{4,6}_connect
EVT_UDP_SENDMSG = 2, // kprobe udp{,v6}_sendmsg
};
struct ConnEvent {
uint32_t pid;
uint32_t tid;
uint32_t uid;
uint32_t gid;
char comm[16];
uint8_t family; // AF_INET=2, AF_INET6=10
uint8_t proto; // IPPROTO_TCP=6, IPPROTO_UDP=17
uint8_t event_kind; // vedi EventKind
uint8_t _pad[1];
uint16_t src_port;
uint16_t dst_port;
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint64_t ts_ns;
};
/*
* Converte un ConnEvent in un bsc::model::Connection parzialmente
* arricchita (process_id, process_comm, user_id, src/dst IP, src/dst
* port gia' valorizzati). Il chiamante (SocketSnapshot o DaemonService)
* completa con process_path, dns, ecc.
*/
bsc::model::Connection event_to_connection(const ConnEvent& ev);
class EBPFMon {
public:
EBPFMon();
~EBPFMon();
EBPFMon(const EBPFMon&) = delete;
EBPFMon& operator=(const EBPFMon&) = delete;
/*
* Carica il programma eBPF e avvia il thread di draining.
* obj_path: percorso del .o compilato dal kernel eBPF.
* Default: /usr/lib/bastionguard-sc/bsc_ebpf.o
* Ritorna true se il setup e' riuscito (anche parziale: se alcuni
* kprobe non possono essere attaccati — es. simbolo assente nel
* kernel — proseguiamo comunque con quelli disponibili).
* Ritorna false solo se non siamo riusciti ad attaccare nemmeno un
* programma o se il load del .o e' fallito.
* In caso di fallimento il daemon prosegue normalmente senza eBPF.
*/
bool start(const std::string& obj_path =
"/usr/lib/bastionguard-sc/bsc_ebpf.o");
/*
* Ferma il thread di draining e fa detach del programma BPF.
* Idempotente (sicuro da chiamare se start() ha fallito).
*/
void stop();
/*
* Consuma tutti gli eventi accumulati dalla ring buffer.
* Scambia atomicamente il buffer interno con uno vuoto — O(1).
* Thread-safe.
*/
std::vector<bsc::model::Connection> drain();
/* true se start() ha avuto successo e il monitor e' attivo */
bool active() const noexcept { return active_.load(); }
private:
void drain_loop(); // corpo del thread background
// Callback C per libbpf ring_buffer__new — deve accedere ai membri privati
friend int ringbuf_callback(void* ctx, void* data, size_t size);
std::atomic_bool active_{false};
std::atomic_bool stop_{false};
std::thread thread_;
mutable std::mutex buf_mutex_;
std::vector<bsc::model::Connection> buf_; // accumulo eventi
// Puntatori opachi a strutture libbpf — non esponiamo libbpf
// all'esterno per non inquinare tutti gli header con #include <bpf/libbpf.h>
void* bpf_obj_{nullptr}; // struct bpf_object*
void* ring_buf_{nullptr}; // struct ring_buffer*
// Manteniamo TUTTI i bpf_link attivi (tracepoint + kprobes) cosi' possiamo
// fare detach in stop(). In v1 ne tenevamo uno solo.
std::vector<void*> links_; // vector<struct bpf_link*>
};
} // namespace bsc::ebpf