137 lines
4.7 KiB
C++
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
|