Skip to content
API Documentation
BastionGuard™
bsc::net::SocketSnapshot Class Reference

#include <SocketSnapshot.hpp>

Classes

struct  SnapshotCache

Public Member Functions

std::vector< bsc::model::Connection > list_connections () const
std::optional< bsc::model::Connection > find_connection (const bsc::model::Connection &needle) const
bool enrich_connection (bsc::model::Connection &conn) const
void invalidate_cache ()
void discard_pending_events () const
bool start_ebpf (const std::string &obj_path="/usr/lib/bastionguard-sc/bsc_ebpf.o")
void stop_ebpf ()

Private Member Functions

std::vector< bsc::model::Connection > collect_connections () const
bool cache_valid (std::chrono::steady_clock::time_point now) const

Private Attributes

std::mutex cache_mutex_
SnapshotCache cache_
bsc::ebpf::EBPFMon ebpf_mon_

Detailed Description

Definition at line 13 of file SocketSnapshot.hpp.

Member Function Documentation

◆ cache_valid()

bool bsc::net::SocketSnapshot::cache_valid ( std::chrono::steady_clock::time_point now) const
nodiscardprivate

Definition at line 338 of file SocketSnapshot.cpp.

338 {
339 return !cache_.connections.empty() && (now - cache_.updated_at) <= kSnapshotTtl;
340}
std::chrono::steady_clock::time_point updated_at
std::vector< bsc::model::Connection > connections

References cache_, bsc::net::SocketSnapshot::SnapshotCache::connections, and bsc::net::SocketSnapshot::SnapshotCache::updated_at.

Referenced by list_connections().

◆ collect_connections()

std::vector< bsc::model::Connection > bsc::net::SocketSnapshot::collect_connections ( ) const
nodiscardprivate

Definition at line 342 of file SocketSnapshot.cpp.

342 {
343 // ── Sorgente eBPF (prioritaria, se attiva) ─────────────────────────
344 // drain() è O(1) e thread-safe: scambia atomicamente il buffer interno
345 // con uno vuoto. Gli eventi eBPF arrivano già con process_id, process_comm
346 // e user_id valorizzati dal kernel — nessuna scansione /proc/*/fd.
347 std::vector<bsc::model::Connection> conns = ebpf_mon_.drain();
349
350 // Costruiamo un set di chiavi già presenti dagli eventi eBPF per evitare
351 // duplicati con i risultati di /proc/net/tcp (stessa connessione vista
352 // da entrambe le sorgenti).
353 std::unordered_set<std::string> ebpf_keys;
354 ebpf_keys.reserve(conns.size());
355 for (const auto& c : conns) ebpf_keys.insert(c.key());
356
357 // ── Sorgente /proc/net (fallback + connessioni preesistenti) ──────
358 auto inode_map = build_inode_pid_map();
359 std::vector<bsc::model::Connection> proc_conns;
360 append_rows(proc_conns, parse_file("/proc/net/tcp", "tcp", false), inode_map);
361 append_rows(proc_conns, parse_file("/proc/net/udp", "udp", false), inode_map);
362 append_rows(proc_conns, parse_file("/proc/net/tcp6", "tcp6", true), inode_map);
363 append_rows(proc_conns, parse_file("/proc/net/udp6", "udp6", true), inode_map);
364
365 // Aggiungi solo i flow /proc non già coperti da eBPF.
366 // Per le connessioni già viste da eBPF, i dati eBPF sono più freschi
367 // (PID e comm catturati nel momento esatto della connect()).
368 for (auto& c : proc_conns) {
369 if (ebpf_keys.find(c.key()) == ebpf_keys.end())
370 conns.push_back(std::move(c));
371 }
372
373 return conns;
374}
std::vector< bsc::model::Connection > drain()
Definition EBPFMon.cpp:286
bsc::ebpf::EBPFMon ebpf_mon_
void ebpf_flow_cache_store_many(const std::vector< bsc::model::Connection > &conns)
void append_rows(std::vector< bsc::model::Connection > &conns, const std::vector< Row > &rows, const std::unordered_map< std::string, std::uint32_t > &inode_map)
std::unordered_map< std::string, std::uint32_t > build_inode_pid_map()
std::vector< Row > parse_file(const std::string &path, const std::string &proto, bool ipv6)

References bsc::ebpf::EBPFMon::drain(), and ebpf_mon_.

Referenced by list_connections().

◆ discard_pending_events()

void bsc::net::SocketSnapshot::discard_pending_events ( ) const

◆ enrich_connection()

bool bsc::net::SocketSnapshot::enrich_connection ( bsc::model::Connection & conn) const

Definition at line 423 of file SocketSnapshot.cpp.

423 {
424 auto pending_ebpf = ebpf_mon_.drain();
425 ebpf_flow_cache_store_many(pending_ebpf);
426 if (auto from_ebpf = ebpf_flow_cache_lookup(conn)) {
427 if (conn.process_id == 0) conn.process_id = from_ebpf->process_id;
428 if (conn.user_id == 0xffffffffu) conn.user_id = from_ebpf->user_id;
429 if (conn.protocol.empty()) conn.protocol = from_ebpf->protocol;
430 if (conn.src_ip.empty()) conn.src_ip = from_ebpf->src_ip;
431 if (conn.src_port == 0) conn.src_port = from_ebpf->src_port;
432 if (conn.dst_ip.empty()) conn.dst_ip = from_ebpf->dst_ip;
433 if (conn.dst_port == 0) conn.dst_port = from_ebpf->dst_port;
434 if (conn.process_comm.empty()) conn.process_comm = from_ebpf->process_comm;
435 if (!from_ebpf->process_path.empty() && conn.process_path.empty()) conn.process_path = from_ebpf->process_path;
436 if (conn.process_id != 0 || !conn.process_comm.empty()) return true;
437 }
438 // ── STRATEGIA 1: eBPF recent-flow cache già drenata sopra ──────────
439 // Se non basta, scendiamo ai fallback tradizionali.
440 // ── STRATEGIA 2: NETLINK_SOCK_DIAG ─────────────────────────────────
441 // Chiede al kernel direttamente l'inode del socket che matcha la
442 // 4-tupla. E' cio' che fa `ss` internamente. Vantaggi critici:
443 // - Nessuna race: il kernel ha sempre l'inode dal momento in cui
444 // il socket e' creato (prima ancora che NFQUEUE veda il SYN).
445 // - Veloce: singola syscall netlink, non scansione di /proc.
446 // - Niente parsing: struttura binaria.
447 // Facciamo questo PRIMA di qualsiasi cosa: se il kernel conosce il
448 // socket, l'inode e' subito buono e serviamo in <1ms.
449 if (!conn.src_ip.empty() && !conn.dst_ip.empty() &&
450 conn.src_port != 0 && conn.dst_port != 0) {
451 const bool is_udp = normalize_proto(conn.protocol) == "udp";
452 auto info = is_udp
453 ? sockdiag_lookup_udp(conn.src_ip, static_cast<std::uint16_t>(conn.src_port),
454 conn.dst_ip, static_cast<std::uint16_t>(conn.dst_port))
455 : sockdiag_lookup_tcp(conn.src_ip, static_cast<std::uint16_t>(conn.src_port),
456 conn.dst_ip, static_cast<std::uint16_t>(conn.dst_port));
457 if (info) {
458 if (conn.user_id == 0xffffffffu) conn.user_id = info->uid;
459 const auto inode_str = std::to_string(info->inode);
460 const auto pid = find_pid_by_inode_direct(inode_str);
461 if (pid != 0) {
462 conn.process_id = pid;
463 if (conn.process_comm.empty()) conn.process_comm = read_proc_comm(pid);
464 return true;
465 }
466 // Inode noto ma PID non ancora mappato in /proc/*/fd: retry
467 // rapido (il kernel potrebbe non aver ancora scritto il
468 // symlink socket:[inode] nel fd del processo).
469 for (int i = 0; i < 3 && conn.process_id == 0; ++i) {
470 std::this_thread::sleep_for(std::chrono::milliseconds(15 + 10 * i));
471 const auto p2 = find_pid_by_inode_direct(inode_str);
472 if (p2 != 0) {
473 conn.process_id = p2;
474 if (conn.process_comm.empty()) conn.process_comm = read_proc_comm(p2);
475 return true;
476 }
477 }
478 }
479 }
480
481 // ── STRATEGIA 2: snapshot /proc/net/tcp cached (fallback) ──────────
482 if (auto found = find_connection(conn)) {
483 if (conn.process_id == 0) conn.process_id = found->process_id;
484 if (conn.user_id == 0xffffffffu) conn.user_id = found->user_id;
485 if (conn.protocol.empty()) conn.protocol = found->protocol;
486 if (conn.src_ip.empty()) conn.src_ip = found->src_ip;
487 if (conn.src_port == 0) conn.src_port = found->src_port;
488 if (conn.dst_ip.empty()) conn.dst_ip = found->dst_ip;
489 if (conn.dst_port == 0) conn.dst_port = found->dst_port;
490 if (conn.process_comm.empty()) conn.process_comm = found->process_comm;
491 if (conn.process_id != 0) {
492 if (conn.process_comm.empty()) conn.process_comm = read_proc_comm(conn.process_id);
493 return true;
494 }
495 }
496
497 // ── STRATEGIA 3: inode da /proc/net/tcp, poi PID da /proc/*/fd ────
498 const auto inode = find_inode_for_flow(conn);
499 if (inode.empty()) {
500 if (conn.process_id != 0 && conn.process_comm.empty()) {
502 }
503 return conn.process_id != 0;
504 }
505
506 const auto pid = find_pid_by_inode_direct(inode);
507 if (pid != 0) {
508 conn.process_id = pid;
509 if (conn.process_comm.empty()) conn.process_comm = read_proc_comm(pid);
510 return true;
511 }
512
513 if (conn.process_id != 0 && conn.process_comm.empty()) {
515 }
516 return conn.process_id != 0;
517}
std::optional< bsc::model::Connection > find_connection(const bsc::model::Connection &needle) const
std::uint32_t find_pid_by_inode_direct(const std::string &inode)
std::optional< bsc::model::Connection > ebpf_flow_cache_lookup(const bsc::model::Connection &needle)
std::string find_inode_for_flow(const bsc::model::Connection &c)
std::optional< SockInfo > sockdiag_lookup_tcp(const std::string &src_ip, std::uint16_t src_port, const std::string &dst_ip, std::uint16_t dst_port)
Definition SockDiag.cpp:118
std::optional< SockInfo > sockdiag_lookup_udp(const std::string &src_ip, std::uint16_t src_port, const std::string &dst_ip, std::uint16_t dst_port)
Definition SockDiag.cpp:130
std::uint32_t process_id
std::uint32_t dst_port
std::uint32_t src_port
std::uint32_t user_id

References bsc::ebpf::EBPFMon::drain(), bsc::model::Connection::dst_ip, bsc::model::Connection::dst_port, ebpf_mon_, find_connection(), bsc::model::Connection::process_comm, bsc::model::Connection::process_id, bsc::model::Connection::process_path, bsc::model::Connection::protocol, bsc::net::sockdiag_lookup_tcp(), bsc::net::sockdiag_lookup_udp(), bsc::model::Connection::src_ip, bsc::model::Connection::src_port, and bsc::model::Connection::user_id.

Referenced by bsc::service::DaemonService::run().

◆ find_connection()

std::optional< bsc::model::Connection > bsc::net::SocketSnapshot::find_connection ( const bsc::model::Connection & needle) const
nodiscard

Definition at line 413 of file SocketSnapshot.cpp.

413 {
414 auto pending_ebpf = ebpf_mon_.drain();
415 ebpf_flow_cache_store_many(pending_ebpf);
416 if (auto cached = ebpf_flow_cache_lookup(needle)) return cached;
417 for (const auto& conn : list_connections()) {
418 if (same_flow(conn, needle)) return conn;
419 }
420 return std::nullopt;
421}
std::vector< bsc::model::Connection > list_connections() const
bool same_flow(const bsc::model::Connection &a, const bsc::model::Connection &b)

References bsc::ebpf::EBPFMon::drain(), ebpf_mon_, and list_connections().

Referenced by enrich_connection().

◆ invalidate_cache()

void bsc::net::SocketSnapshot::invalidate_cache ( )

◆ list_connections()

std::vector< bsc::model::Connection > bsc::net::SocketSnapshot::list_connections ( ) const
nodiscard

Definition at line 384 of file SocketSnapshot.cpp.

384 {
385 const auto now = std::chrono::steady_clock::now();
386 {
387 std::lock_guard lock(cache_mutex_);
388 if (cache_valid(now)) return cache_.connections;
389 }
390
391 auto refreshed = collect_connections();
392 {
393 std::lock_guard lock(cache_mutex_);
394 cache_.connections = refreshed;
396 return cache_.connections;
397 }
398}
bool cache_valid(std::chrono::steady_clock::time_point now) const
std::vector< bsc::model::Connection > collect_connections() const

References cache_, cache_mutex_, cache_valid(), collect_connections(), bsc::net::SocketSnapshot::SnapshotCache::connections, and bsc::net::SocketSnapshot::SnapshotCache::updated_at.

Referenced by find_connection().

◆ start_ebpf()

bool bsc::net::SocketSnapshot::start_ebpf ( const std::string & obj_path = "/usr/lib/bastionguard-sc/bsc_ebpf.o")

Definition at line 376 of file SocketSnapshot.cpp.

376 {
377 return ebpf_mon_.start(obj_path);
378}
bool start(const std::string &obj_path="/usr/lib/bastionguard-sc/bsc_ebpf.o")
Definition EBPFMon.cpp:277

References ebpf_mon_, and bsc::ebpf::EBPFMon::start().

Referenced by bsc::service::DaemonService::run().

◆ stop_ebpf()

void bsc::net::SocketSnapshot::stop_ebpf ( )

Definition at line 380 of file SocketSnapshot.cpp.

380 {
381 ebpf_mon_.stop();
382}

References ebpf_mon_, and bsc::ebpf::EBPFMon::stop().

Member Data Documentation

◆ cache_

SnapshotCache bsc::net::SocketSnapshot::cache_
mutableprivate

◆ cache_mutex_

std::mutex bsc::net::SocketSnapshot::cache_mutex_
mutableprivate

Definition at line 45 of file SocketSnapshot.hpp.

Referenced by discard_pending_events(), invalidate_cache(), and list_connections().

◆ ebpf_mon_

bsc::ebpf::EBPFMon bsc::net::SocketSnapshot::ebpf_mon_
mutableprivate

The documentation for this class was generated from the following files:
  • BastionGuard/thirdparty/bastionguard-secure-connection/daemon/include/bsc/net/SocketSnapshot.hpp
  • BastionGuard/thirdparty/bastionguard-secure-connection/daemon/src/net/SocketSnapshot.cpp