Skip to content
API Documentation
BastionGuard™
BastionGuard::WireguardBackend Class Reference

#include <WireguardBackend.hpp>

Public Member Functions

 WireguardBackend (std::string iface="wg0")
bool tooling_available (std::string *missing=nullptr) const
bool load_from_conf (const std::string &conf_path, std::string *err=nullptr)
bool load_from_params (const WgConfigParams &p, std::string *err=nullptr)
bool has_spec () const
std::string configured_endpoint () const
std::optional< WgKeyPair > generate_keys () const
OpResult connect ()
OpResult disconnect ()
WgStatus status () const
std::optional< std::string > public_ip () const
OpResult killswitch_enable ()
OpResult killswitch_disable ()
bool killswitch_active () const
const std::string & interface_name () const

Private Member Functions

OpResult run (const std::vector< std::string > &argv, bool privileged) const
OpResult run_with_stdin (const std::vector< std::string > &argv, const std::string &stdin_data, bool privileged) const
std::string build_wg_setconf_text () const

Private Attributes

std::string iface_
WgTunnelSpec spec_

Detailed Description

Definition at line 95 of file WireguardBackend.hpp.

Constructor & Destructor Documentation

◆ WireguardBackend()

BastionGuard::WireguardBackend::WireguardBackend ( std::string iface = "wg0")
explicit

Definition at line 27 of file WireguardBackend.cpp.

28 : iface_(std::move(iface)) {}

References iface_.

Member Function Documentation

◆ build_wg_setconf_text()

std::string BastionGuard::WireguardBackend::build_wg_setconf_text ( ) const
private

Definition at line 371 of file WireguardBackend.cpp.

371 {
372 std::ostringstream os;
373 os << "[Interface]\n";
374 os << "PrivateKey = " << spec_.private_key << "\n";
375 if (!spec_.listen_port.empty()) os << "ListenPort = " << spec_.listen_port << "\n";
376 for (const auto& p : spec_.peers) {
377 os << "\n[Peer]\n";
378 os << "PublicKey = " << p.public_key << "\n";
379 if (!p.preshared_key.empty())
380 os << "PresharedKey = " << p.preshared_key << "\n";
381 if (!p.endpoint.empty())
382 os << "Endpoint = " << p.endpoint << "\n";
383 if (!p.allowed_ips.empty())
384 os << "AllowedIPs = " << p.allowed_ips << "\n";
385 if (!p.persistent_keepalive.empty())
386 os << "PersistentKeepalive = " << p.persistent_keepalive << "\n";
387 }
388 return os.str();
389}
std::vector< WgPeer > peers

References BastionGuard::WgTunnelSpec::listen_port, BastionGuard::WgTunnelSpec::peers, BastionGuard::WgTunnelSpec::private_key, and spec_.

Referenced by connect().

◆ configured_endpoint()

std::string BastionGuard::WireguardBackend::configured_endpoint ( ) const
inline

Definition at line 115 of file WireguardBackend.hpp.

115 {
116 return spec_.peers.empty() ? std::string{} : spec_.peers[0].endpoint;
117 }

References BastionGuard::WgTunnelSpec::peers, and spec_.

◆ connect()

OpResult BastionGuard::WireguardBackend::connect ( )

Definition at line 404 of file WireguardBackend.cpp.

404 {
405 OpResult res;
406 std::string validation_error;
407 if (!validate_spec_(spec_, &validation_error) || !valid_iface_(iface_)) {
408 res.ok = false;
409 res.exit_code = -1;
410 res.output = validation_error.empty() ? "Interfaccia WireGuard non valida." : validation_error;
411 return res;
412 }
413
414 auto fail = [](const std::string& step, const OpResult& r) {
415 OpResult out = r;
416 out.ok = false;
417 out.output = step + (r.output.empty() ? std::string{} : ": " + r.output);
418 return out;
419 };
420
421 // Cleanup residue from an interrupted previous connection. Errors here are harmless.
422 run({"ip", "link", "delete", "dev", iface_}, true);
423
424 OpResult r = run({"ip", "link", "add", "dev", iface_, "type", "wireguard"}, true);
425 if (!r.ok) return fail("ip link add", r);
426
427 const std::string wg_text = build_wg_setconf_text();
428 r = run_with_stdin({"wg", "setconf", iface_, "/dev/stdin"}, wg_text, true);
429 if (!r.ok) {
430 run({"ip", "link", "delete", "dev", iface_}, true);
431 return fail("wg setconf", r);
432 }
433
434 if (spec_.mtu > 0) {
435 r = run({"ip", "link", "set", "dev", iface_, "mtu", std::to_string(spec_.mtu)}, true);
436 if (!r.ok) return fail("ip link mtu", r);
437 }
438
439 for (const auto& a : spec_.addresses) {
440 const std::string fam = (a.find(':') != std::string::npos) ? "-6" : "-4";
441 r = run({"ip", fam, "address", "add", a, "dev", iface_}, true);
442 if (!r.ok) return fail("ip address add", r);
443 }
444
445 r = run({"ip", "link", "set", "dev", iface_, "up"}, true);
446 if (!r.ok) return fail("ip link up", r);
447
449 // These may already exist after a previous interrupted run; preserve the
450 // old `|| true` semantics by intentionally ignoring duplicate errors.
451 run({"ip", "-4", "route", "add", "default", "dev", iface_, "table", "51820"}, true);
452 run({"ip", "-4", "rule", "add", "not", "from", "all", "fwmark", "0x51820",
453 "lookup", "51820", "priority", "32764"}, true);
454 run({"ip", "-4", "rule", "add", "table", "main", "suppress_prefixlength", "0",
455 "priority", "32765"}, true);
456 run({"wg", "set", iface_, "fwmark", "0x51820"}, true);
457 run({"sysctl", "-q", "net.ipv4.conf.all.src_valid_mark=1"}, true);
458 } else {
459 for (const auto& peer : spec_.peers) {
460 for (const auto& cidr : split_csv_(peer.allowed_ips)) {
461 if (cidr == "0.0.0.0/0" || cidr == "::/0") continue;
462 const std::string fam = (cidr.find(':') != std::string::npos) ? "-6" : "-4";
463 run({"ip", fam, "route", "add", cidr, "dev", iface_}, true);
464 }
465 }
466 }
467
468 if (!spec_.dns.empty()) {
469 static const std::string bak = "/etc/resolv.conf.bastionguard.bak";
470 if (!fs::exists(bak)) {
471 run({"cp", "-a", "--no-dereference", "--", "/etc/resolv.conf", bak}, true);
472 }
473
474 std::ostringstream dns_text;
475 for (const auto& ns : spec_.dns)
476 dns_text << "nameserver " << ns << "\n";
477
478 r = run_with_stdin({"tee", "/etc/resolv.conf"}, dns_text.str(), true);
479 if (!r.ok) return fail("aggiornamento DNS", r);
480 }
481
482 res.ok = true;
483 res.exit_code = 0;
484 res.output = "Tunnel WireGuard attivato.";
485 return res;
486}
OpResult run(const std::vector< std::string > &argv, bool privileged) const
OpResult run_with_stdin(const std::vector< std::string > &argv, const std::string &stdin_data, bool privileged) const
std::string build_wg_setconf_text() const
static bool validate_spec_(const WgTunnelSpec &sp, std::string *err)
static std::vector< std::string > split_csv_(const std::string &s)
static bool valid_iface_(const std::string &iface)
static bool needs_default_route_(const WgTunnelSpec &sp)
void fail(const std::string &message)
std::vector< std::string > dns
std::vector< std::string > addresses

References BastionGuard::WgTunnelSpec::addresses, build_wg_setconf_text(), BastionGuard::WgTunnelSpec::dns, BastionGuard::OpResult::exit_code, iface_, BastionGuard::WgTunnelSpec::mtu, BastionGuard::needs_default_route_(), BastionGuard::OpResult::ok, BastionGuard::OpResult::output, BastionGuard::WgTunnelSpec::peers, run(), run_with_stdin(), spec_, BastionGuard::split_csv_(), BastionGuard::valid_iface_(), and BastionGuard::validate_spec_().

◆ disconnect()

OpResult BastionGuard::WireguardBackend::disconnect ( )

Definition at line 491 of file WireguardBackend.cpp.

491 {
492 static const std::string bak = "/etc/resolv.conf.bastionguard.bak";
493
494 if (fs::exists(bak)) {
495 run({"rm", "-f", "--", "/etc/resolv.conf"}, true);
496 run({"cp", "-a", "--no-dereference", "--", bak, "/etc/resolv.conf"}, true);
497 run({"rm", "-f", "--", bak}, true);
498 }
499
500 run({"ip", "-4", "rule", "del", "table", "main", "suppress_prefixlength", "0",
501 "priority", "32765"}, true);
502 run({"ip", "-4", "rule", "del", "not", "from", "all", "fwmark", "0x51820",
503 "lookup", "51820", "priority", "32764"}, true);
504 run({"ip", "-4", "route", "flush", "table", "51820"}, true);
505 run({"ip", "link", "delete", "dev", iface_}, true);
506
507 OpResult check = run({"ip", "link", "show", iface_}, false);
508 OpResult res;
509 res.ok = !check.ok;
510 res.exit_code = res.ok ? 0 : 1;
511 res.output = res.ok ? "Tunnel WireGuard disattivato." : "Interfaccia WireGuard ancora presente.";
512 return res;
513}

References iface_, BastionGuard::OpResult::ok, and run().

◆ generate_keys()

std::optional< WgKeyPair > BastionGuard::WireguardBackend::generate_keys ( ) const

Definition at line 356 of file WireguardBackend.cpp.

356 {
357 OpResult gen = run({"wg", "genkey"}, false);
358 if (!gen.ok) return std::nullopt;
359 std::string priv = strip_(gen.output);
360 if (priv.empty()) return std::nullopt;
361 OpResult pub = run_with_stdin({"wg", "pubkey"}, priv + "\n", false);
362 if (!pub.ok) return std::nullopt;
363 std::string pk = strip_(pub.output);
364 if (pk.empty()) return std::nullopt;
365 return WgKeyPair{priv, pk};
366}
static std::string strip_(std::string s)

References BastionGuard::OpResult::ok, BastionGuard::OpResult::output, run(), run_with_stdin(), and BastionGuard::strip_().

◆ has_spec()

bool BastionGuard::WireguardBackend::has_spec ( ) const
inline

Definition at line 111 of file WireguardBackend.hpp.

111{ return spec_.valid(); }

References spec_, and BastionGuard::WgTunnelSpec::valid().

◆ interface_name()

const std::string & BastionGuard::WireguardBackend::interface_name ( ) const
inline

Definition at line 137 of file WireguardBackend.hpp.

137{ return iface_; }

References iface_.

◆ killswitch_active()

bool BastionGuard::WireguardBackend::killswitch_active ( ) const

Definition at line 618 of file WireguardBackend.cpp.

618 {
619 return run({"nft", "list", "table", "inet", kKsTable}, true).ok;
620}
static const char * kKsTable

References BastionGuard::kKsTable, and run().

◆ killswitch_disable()

OpResult BastionGuard::WireguardBackend::killswitch_disable ( )

Definition at line 608 of file WireguardBackend.cpp.

608 {
609 OpResult r = run({"nft", "delete", "table", "inet", kKsTable}, true);
610 if (!r.ok) {
611 // An already absent table is equivalent to a disabled kill-switch.
612 r.ok = true;
613 r.exit_code = 0;
614 }
615 return r;
616}

References BastionGuard::kKsTable, and run().

◆ killswitch_enable()

OpResult BastionGuard::WireguardBackend::killswitch_enable ( )

Definition at line 570 of file WireguardBackend.cpp.

570 {
571 if (!valid_iface_(iface_))
572 return OpResult{false, -1, "Interfaccia WireGuard non valida."};
573
574 WgStatus st = status();
575 std::string endpoint_rule;
576 if (!st.endpoint.empty()) {
577 std::string host;
578 if (st.endpoint.front() == '[') {
579 auto close = st.endpoint.find(']');
580 if (close != std::string::npos) host = st.endpoint.substr(1, close - 1);
581 } else {
582 auto pos = st.endpoint.rfind(':');
583 if (pos != std::string::npos) host = st.endpoint.substr(0, pos);
584 }
585
586 struct in_addr a4 {};
587 struct in6_addr a6 {};
588 if (::inet_pton(AF_INET, host.c_str(), &a4) == 1)
589 endpoint_rule = " ip daddr " + host + " accept\n";
590 else if (::inet_pton(AF_INET6, host.c_str(), &a6) == 1)
591 endpoint_rule = " ip6 daddr " + host + " accept\n";
592 }
593
594 std::ostringstream rules;
595 rules << "table inet " << kKsTable << " {\n"
596 << " chain out {\n"
597 << " type filter hook output priority 0; policy drop;\n"
598 << " oifname lo accept\n"
599 << " oifname \"" << iface_ << "\" accept\n"
600 << endpoint_rule
601 << " meta l4proto udp accept\n"
602 << " }\n"
603 << "}\n";
604
605 return run_with_stdin({"nft", "-f", "-"}, rules.str(), true);
606}
#define AF_INET
Definition bsc_ebpf.c:56
#define AF_INET6
Definition bsc_ebpf.c:57

References AF_INET, AF_INET6, BastionGuard::WgStatus::endpoint, iface_, BastionGuard::kKsTable, run_with_stdin(), status(), and BastionGuard::valid_iface_().

◆ load_from_conf()

bool BastionGuard::WireguardBackend::load_from_conf ( const std::string & conf_path,
std::string * err = nullptr )

Definition at line 266 of file WireguardBackend.cpp.

267 {
268 std::ifstream in(conf_path);
269 if (!in) { if (err) *err = "Impossibile aprire: " + conf_path; return false; }
270
271 WgTunnelSpec sp;
272 enum class Section { None, Interface, Peer } sec = Section::None;
273 WgPeer cur_peer;
274
275 auto flush_peer = [&]() {
276 if (!cur_peer.public_key.empty() || !cur_peer.endpoint.empty()) {
277 sp.peers.push_back(cur_peer);
278 cur_peer = {};
279 }
280 };
281
282 std::string line;
283 while (std::getline(in, line)) {
284 std::string t = strip_(line);
285 if (t.empty() || t[0] == '#' || t[0] == ';') continue;
286
287 if (t.front() == '[' && t.back() == ']') {
288 std::string name = t.substr(1, t.size() - 2);
289 // normalizza
290 for (auto& c : name) c = (char)std::tolower((unsigned char)c);
291 if (name == "interface") { flush_peer(); sec = Section::Interface; }
292 else if (name == "peer") { flush_peer(); sec = Section::Peer; }
293 else sec = Section::None;
294 continue;
295 }
296
297 // key = value (con qualunque spaziatura)
298 size_t eq = t.find('=');
299 if (eq == std::string::npos) continue;
300 std::string k = strip_(t.substr(0, eq));
301 std::string v = strip_(t.substr(eq + 1));
302 std::string kl = k;
303 for (auto& c : kl) c = (char)std::tolower((unsigned char)c);
304
305 if (sec == Section::Interface) {
306 if (kl == "privatekey") sp.private_key = v;
307 else if (kl == "address") sp.addresses = split_csv_(v);
308 else if (kl == "dns") sp.dns = split_csv_(v);
309 else if (kl == "listenport") sp.listen_port = v;
310 else if (kl == "mtu") sp.mtu = std::atoi(v.c_str());
311 // PostUp/PostDown/Table/SaveConfig: IGNORATI (sono cose di wg-quick)
312 } else if (sec == Section::Peer) {
313 if (kl == "publickey") cur_peer.public_key = v;
314 else if (kl == "presharedkey") cur_peer.preshared_key = v;
315 else if (kl == "endpoint") cur_peer.endpoint = v;
316 else if (kl == "allowedips") cur_peer.allowed_ips = v;
317 else if (kl == "persistentkeepalive") cur_peer.persistent_keepalive = v;
318 }
319 }
320 flush_peer();
321
322 if (!validate_spec_(sp, err)) return false;
323 spec_ = std::move(sp);
324 return true;
325}

References BastionGuard::WgTunnelSpec::addresses, BastionGuard::WgPeer::allowed_ips, BastionGuard::WgTunnelSpec::dns, BastionGuard::WgPeer::endpoint, BastionGuard::WgTunnelSpec::listen_port, BastionGuard::WgTunnelSpec::mtu, BastionGuard::WgTunnelSpec::peers, BastionGuard::WgPeer::persistent_keepalive, BastionGuard::WgPeer::preshared_key, BastionGuard::WgTunnelSpec::private_key, BastionGuard::WgPeer::public_key, spec_, BastionGuard::split_csv_(), BastionGuard::strip_(), and BastionGuard::validate_spec_().

◆ load_from_params()

bool BastionGuard::WireguardBackend::load_from_params ( const WgConfigParams & p,
std::string * err = nullptr )

Definition at line 327 of file WireguardBackend.cpp.

328 {
329 if (p.private_key.empty() || p.server_public_key.empty() ||
330 p.endpoint.empty() || p.address.empty()) {
331 if (err) *err = "Campi obbligatori mancanti.";
332 return false;
333 }
334 WgTunnelSpec sp;
335 sp.private_key = p.private_key;
336 sp.addresses = split_csv_(p.address);
337 if (!p.dns.empty()) sp.dns = split_csv_(p.dns);
338 sp.listen_port = p.listen_port;
339
340 WgPeer peer;
341 peer.public_key = p.server_public_key;
342 peer.endpoint = p.endpoint;
343 peer.allowed_ips = p.allowed_ips.empty() ? std::string("0.0.0.0/0, ::/0")
344 : p.allowed_ips;
345 peer.persistent_keepalive = p.persistent_keepalive;
346 sp.peers.push_back(peer);
347
348 if (!validate_spec_(sp, err)) return false;
349 spec_ = std::move(sp);
350 return true;
351}

References BastionGuard::WgConfigParams::address, BastionGuard::WgTunnelSpec::addresses, BastionGuard::WgConfigParams::allowed_ips, BastionGuard::WgPeer::allowed_ips, BastionGuard::WgConfigParams::dns, BastionGuard::WgTunnelSpec::dns, BastionGuard::WgConfigParams::endpoint, BastionGuard::WgPeer::endpoint, BastionGuard::WgConfigParams::listen_port, BastionGuard::WgTunnelSpec::listen_port, BastionGuard::WgTunnelSpec::peers, BastionGuard::WgConfigParams::persistent_keepalive, BastionGuard::WgPeer::persistent_keepalive, BastionGuard::WgConfigParams::private_key, BastionGuard::WgTunnelSpec::private_key, BastionGuard::WgPeer::public_key, BastionGuard::WgConfigParams::server_public_key, spec_, BastionGuard::split_csv_(), and BastionGuard::validate_spec_().

◆ public_ip()

std::optional< std::string > BastionGuard::WireguardBackend::public_ip ( ) const

Definition at line 545 of file WireguardBackend.cpp.

545 {
546 // Proviamo più servizi: se uno è lento/bloccato, passiamo al successivo.
547 static const char* endpoints[] = {
548 "https://api.ipify.org",
549 "https://ifconfig.me/ip",
550 "https://icanhazip.com",
551 "https://ipinfo.io/ip",
552 };
553 for (const char* url : endpoints) {
554 OpResult r = run({"curl", "-s", "-4", "--max-time", "10", url}, false);
555 if (r.ok) {
556 std::string ip = strip_(r.output);
557 // sanity: deve assomigliare a un IPv4
558 if (!ip.empty() && ip.find_first_not_of("0123456789.") == std::string::npos)
559 return ip;
560 }
561 }
562 return std::nullopt;
563}

References run(), and BastionGuard::strip_().

◆ run()

OpResult BastionGuard::WireguardBackend::run ( const std::vector< std::string > & argv,
bool privileged ) const
private

Definition at line 33 of file WireguardBackend.cpp.

34 {
35 OpResult res;
36 std::vector<std::string> full;
37 if (privileged) full.push_back("pkexec");
38 full.insert(full.end(), argv.begin(), argv.end());
39
40 std::string out, err;
41 int exit_status = 0;
42 try {
43 Glib::spawn_sync("", full, Glib::SpawnFlags::SEARCH_PATH,
44 {}, &out, &err, &exit_status);
45 } catch (const Glib::Error& e) {
46 res.ok = false; res.exit_code = -1;
47 res.output = std::string("spawn error: ") + e.what();
48 return res;
49 }
50 res.exit_code = exit_status;
51 res.ok = (exit_status == 0);
52 res.output = out;
53 if (!err.empty()) { if (!res.output.empty()) res.output += "\n"; res.output += err; }
54 return res;
55}

References BastionGuard::OpResult::exit_code, BastionGuard::OpResult::ok, and BastionGuard::OpResult::output.

Referenced by connect(), disconnect(), generate_keys(), killswitch_active(), killswitch_disable(), public_ip(), and status().

◆ run_with_stdin()

OpResult BastionGuard::WireguardBackend::run_with_stdin ( const std::vector< std::string > & argv,
const std::string & stdin_data,
bool privileged ) const
private

Definition at line 57 of file WireguardBackend.cpp.

59 {
60 OpResult res;
61 std::vector<std::string> full;
62 if (privileged) full.push_back("pkexec");
63 full.insert(full.end(), argv.begin(), argv.end());
64
65 std::vector<char*> c_argv;
66 c_argv.reserve(full.size() + 1);
67 for (auto& s : full) c_argv.push_back(const_cast<char*>(s.c_str()));
68 c_argv.push_back(nullptr);
69
70 int in_pipe[2], out_pipe[2];
71 if (pipe(in_pipe) != 0 || pipe(out_pipe) != 0) {
72 res.ok = false; res.output = "pipe() fallita"; return res;
73 }
74 pid_t pid = fork();
75 if (pid < 0) { res.ok = false; res.output = "fork() fallita"; return res; }
76 if (pid == 0) {
77 dup2(in_pipe[0], STDIN_FILENO);
78 dup2(out_pipe[1], STDOUT_FILENO);
79 dup2(out_pipe[1], STDERR_FILENO);
80 close(in_pipe[0]); close(in_pipe[1]);
81 close(out_pipe[0]); close(out_pipe[1]);
82 execvp(c_argv[0], c_argv.data());
83 _exit(127);
84 }
85 close(in_pipe[0]); close(out_pipe[1]);
86 size_t w = 0;
87 while (w < stdin_data.size()) {
88 ssize_t n = write(in_pipe[1], stdin_data.data() + w, stdin_data.size() - w);
89 if (n <= 0) break;
90 w += (size_t)n;
91 }
92 close(in_pipe[1]);
93
94 std::string output; char buf[4096]; ssize_t n;
95 while ((n = read(out_pipe[0], buf, sizeof(buf))) > 0)
96 output.append(buf, (size_t)n);
97 close(out_pipe[0]);
98
99 int status = 0; waitpid(pid, &status, 0);
100 res.output = output;
101 if (WIFEXITED(status)) { res.exit_code = WEXITSTATUS(status); res.ok = (res.exit_code == 0); }
102 else { res.exit_code = -1; res.ok = false; }
103 return res;
104}

References BastionGuard::OpResult::exit_code, BastionGuard::OpResult::ok, BastionGuard::OpResult::output, and status().

Referenced by connect(), generate_keys(), and killswitch_enable().

◆ status()

WgStatus BastionGuard::WireguardBackend::status ( ) const

Definition at line 518 of file WireguardBackend.cpp.

518 {
519 WgStatus st; st.interface = iface_;
520
521 // Rilevamento "up/down" primario SENZA privilegi: l'esistenza
522 // dell'interfaccia wg0 con stato UP è leggibile da `ip link show`,
523 // che non richiede root (a differenza di `wg show` su molti sistemi).
524 OpResult link = run({"ip", "link", "show", iface_}, false);
525 if (!link.ok) { st.up = false; return st; } // interfaccia assente
526 st.up = true;
527
528 // Dettagli (endpoint, handshake) da `wg show`: utile ma non essenziale,
529 // e può richiedere privilegi. Proviamo senza, poi con pkexec solo se
530 // l'output sembra negato. Se fallisce, lasciamo st.up=true comunque.
531 OpResult r = run({"wg", "show", iface_}, false);
532 if (!r.ok) return st; // niente dettagli, ma il tunnel è su
533 std::istringstream iss(r.output);
534 std::string line;
535 while (std::getline(iss, line)) {
536 std::string t = strip_(line);
537 if (t.rfind("endpoint:", 0) == 0)
538 st.endpoint = strip_(t.substr(9));
539 else if (t.rfind("latest handshake:", 0) == 0)
540 st.last_handshake = strip_(t.substr(17));
541 }
542 return st;
543}

References BastionGuard::WgStatus::endpoint, iface_, BastionGuard::WgStatus::interface, BastionGuard::WgStatus::last_handshake, BastionGuard::OpResult::ok, run(), BastionGuard::strip_(), and BastionGuard::WgStatus::up.

Referenced by killswitch_enable(), and run_with_stdin().

◆ tooling_available()

bool BastionGuard::WireguardBackend::tooling_available ( std::string * missing = nullptr) const

Definition at line 125 of file WireguardBackend.cpp.

125 {
126 if (!in_path_("wg")) { if (missing) *missing = "wg"; return false; }
127 if (!in_path_("ip")) { if (missing) *missing = "ip"; return false; }
128 return true;
129}
static bool in_path_(const char *bin)

References BastionGuard::in_path_().

Member Data Documentation

◆ iface_

std::string BastionGuard::WireguardBackend::iface_
private

◆ spec_

WgTunnelSpec BastionGuard::WireguardBackend::spec_
private

The documentation for this class was generated from the following files: