Skip to content
API Documentation
BastionGuard™
WireguardBackend.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 *
5 * GPL v3. BastionGuard™ is a trademark of Calogero Scarnà.
6 */
7
9
10#include <glibmm/spawn.h>
11#include <glibmm/shell.h>
12
13#include <cstdlib>
14#include <cstring>
15#include <fstream>
16#include <sstream>
17#include <filesystem>
18#include <regex>
19#include <arpa/inet.h>
20#include <unistd.h>
21#include <sys/wait.h>
22
23namespace fs = std::filesystem;
24
25namespace BastionGuard {
26
28 : iface_(std::move(iface)) {}
29
30// ===========================================================================
31// Esecuzione processi (stesso meccanismo di prima)
32// ===========================================================================
33OpResult WireguardBackend::run(const std::vector<std::string>& argv,
34 bool privileged) const {
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}
56
57OpResult WireguardBackend::run_with_stdin(const std::vector<std::string>& argv,
58 const std::string& stdin_data,
59 bool privileged) const {
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}
105
106// ===========================================================================
107// Tooling
108// ===========================================================================
109static bool in_path_(const char* bin) {
110 const char* p = std::getenv("PATH");
111 if (!p) p = "/usr/sbin:/usr/bin:/sbin:/bin";
112 std::string paths(p);
113 size_t start = 0;
114 while (start <= paths.size()) {
115 size_t end = paths.find(':', start);
116 std::string dir = paths.substr(start,
117 end == std::string::npos ? std::string::npos : end - start);
118 if (!dir.empty() && ::access((dir + "/" + bin).c_str(), X_OK) == 0) return true;
119 if (end == std::string::npos) break;
120 start = end + 1;
121 }
122 return false;
123}
124
125bool WireguardBackend::tooling_available(std::string* missing) const {
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}
130
131// ===========================================================================
132// Parser del file .conf (robusto: spazi, commenti, multi-peer)
133// ===========================================================================
134static std::string strip_(std::string s) {
135 size_t a = s.find_first_not_of(" \t\r\n");
136 size_t b = s.find_last_not_of(" \t\r\n");
137 if (a == std::string::npos) return {};
138 return s.substr(a, b - a + 1);
139}
140
141static std::vector<std::string> split_csv_(const std::string& s) {
142 std::vector<std::string> out;
143 std::string tok; std::istringstream iss(s);
144 while (std::getline(iss, tok, ',')) {
145 std::string t = strip_(tok);
146 if (!t.empty()) out.push_back(t);
147 }
148 return out;
149}
150
151static bool parse_uint_range_(const std::string& s, unsigned min, unsigned max) {
152 if (s.empty() || s.find_first_not_of("0123456789") != std::string::npos)
153 return false;
154 try {
155 unsigned long v = std::stoul(s);
156 return v >= min && v <= max;
157 } catch (...) {
158 return false;
159 }
160}
161
162static bool valid_wg_key_(const std::string& s) {
163 // WireGuard keys are 32 bytes encoded as 44-character base64 strings.
164 static const std::regex re(R"(^[A-Za-z0-9+/]{43}=$)");
165 return std::regex_match(s, re);
166}
167
168static bool valid_ip_(const std::string& s) {
169 struct in_addr a4 {};
170 struct in6_addr a6 {};
171 return ::inet_pton(AF_INET, s.c_str(), &a4) == 1 ||
172 ::inet_pton(AF_INET6, s.c_str(), &a6) == 1;
173}
174
175static bool valid_cidr_(const std::string& s) {
176 const auto slash = s.rfind('/');
177 if (slash == std::string::npos || slash == 0 || slash + 1 >= s.size())
178 return false;
179 const std::string ip = s.substr(0, slash);
180 const std::string prefix = s.substr(slash + 1);
181 if (!parse_uint_range_(prefix, 0, 128)) return false;
182
183 struct in_addr a4 {};
184 if (::inet_pton(AF_INET, ip.c_str(), &a4) == 1)
185 return std::stoul(prefix) <= 32;
186 struct in6_addr a6 {};
187 if (::inet_pton(AF_INET6, ip.c_str(), &a6) == 1)
188 return std::stoul(prefix) <= 128;
189 return false;
190}
191
192static bool valid_hostname_(const std::string& host) {
193 if (host.empty() || host.size() > 253 || host.front() == '.' || host.back() == '.')
194 return false;
195 static const std::regex re(R"(^[A-Za-z0-9.-]+$)");
196 if (!std::regex_match(host, re) || host.find("..") != std::string::npos)
197 return false;
198 std::istringstream labels(host);
199 std::string label;
200 while (std::getline(labels, label, '.')) {
201 if (label.empty() || label.size() > 63 || label.front() == '-' || label.back() == '-')
202 return false;
203 }
204 return true;
205}
206
207static bool valid_endpoint_(const std::string& endpoint) {
208 std::string host, port;
209 if (!endpoint.empty() && endpoint.front() == '[') {
210 const auto close = endpoint.find(']');
211 if (close == std::string::npos || close + 1 >= endpoint.size() || endpoint[close + 1] != ':')
212 return false;
213 host = endpoint.substr(1, close - 1);
214 port = endpoint.substr(close + 2);
215 struct in6_addr a6 {};
216 if (::inet_pton(AF_INET6, host.c_str(), &a6) != 1) return false;
217 } else {
218 const auto colon = endpoint.rfind(':');
219 if (colon == std::string::npos || colon == 0 || colon + 1 >= endpoint.size())
220 return false;
221 host = endpoint.substr(0, colon);
222 port = endpoint.substr(colon + 1);
223 struct in_addr a4 {};
224 if (::inet_pton(AF_INET, host.c_str(), &a4) != 1 && !valid_hostname_(host))
225 return false;
226 }
227 return parse_uint_range_(port, 1, 65535);
228}
229
230static bool valid_iface_(const std::string& iface) {
231 static const std::regex re(R"(^[A-Za-z0-9_.-]{1,15}$)");
232 return std::regex_match(iface, re);
233}
234
235static bool validate_spec_(const WgTunnelSpec& sp, std::string* err) {
236 auto fail = [&](const char* msg) {
237 if (err) *err = msg;
238 return false;
239 };
240 if (!sp.valid()) return fail("Configurazione WireGuard incompleta.");
241 if (!valid_wg_key_(sp.private_key)) return fail("PrivateKey WireGuard non valida.");
242 if (!sp.listen_port.empty() && !parse_uint_range_(sp.listen_port, 1, 65535))
243 return fail("ListenPort non valida.");
244 if (sp.mtu != 0 && (sp.mtu < 576 || sp.mtu > 9000))
245 return fail("MTU fuori intervallo consentito.");
246
247 for (const auto& a : sp.addresses)
248 if (!valid_cidr_(a)) return fail("Address deve essere un IP/CIDR valido.");
249 for (const auto& ns : sp.dns)
250 if (!valid_ip_(ns)) return fail("DNS deve essere un indirizzo IP valido.");
251
252 for (const auto& peer : sp.peers) {
253 if (!valid_wg_key_(peer.public_key)) return fail("PublicKey peer non valida.");
254 if (!peer.preshared_key.empty() && !valid_wg_key_(peer.preshared_key))
255 return fail("PresharedKey non valida.");
256 if (!valid_endpoint_(peer.endpoint)) return fail("Endpoint WireGuard non valido.");
257 for (const auto& cidr : split_csv_(peer.allowed_ips))
258 if (!valid_cidr_(cidr)) return fail("AllowedIPs contiene un CIDR non valido.");
259 if (!peer.persistent_keepalive.empty() &&
260 !parse_uint_range_(peer.persistent_keepalive, 0, 65535))
261 return fail("PersistentKeepalive non valido.");
262 }
263 return true;
264}
265
266bool WireguardBackend::load_from_conf(const std::string& conf_path,
267 std::string* err) {
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}
326
328 std::string* err) {
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;
337 if (!p.dns.empty()) sp.dns = split_csv_(p.dns);
339
340 WgPeer peer;
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;
346 sp.peers.push_back(peer);
347
348 if (!validate_spec_(sp, err)) return false;
349 spec_ = std::move(sp);
350 return true;
351}
352
353// ===========================================================================
354// Chiavi
355// ===========================================================================
356std::optional<WgKeyPair> WireguardBackend::generate_keys() const {
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}
367
368// ===========================================================================
369// Build del testo per `wg setconf` (solo cose che `wg` capisce)
370// ===========================================================================
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}
390
391// ===========================================================================
392// Helper: determina se serve full-tunnel (default route via wg0)
393// ===========================================================================
394static bool needs_default_route_(const WgTunnelSpec& sp) {
395 for (const auto& p : sp.peers)
396 for (const auto& a : split_csv_(p.allowed_ips))
397 if (a == "0.0.0.0/0" || a == "::/0") return true;
398 return false;
399}
400
401// ===========================================================================
402// CONNECT: setup completo con ip + wg, niente wg-quick
403// ===========================================================================
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}
487
488// ===========================================================================
489// DISCONNECT: ripristina resolv.conf, rimuove route, butta giù interfaccia
490// ===========================================================================
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}
514
515// ===========================================================================
516// Stato / IP pubblico
517// ===========================================================================
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}
544
545std::optional<std::string> WireguardBackend::public_ip() const {
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}
564
565// ===========================================================================
566// Kill-switch (invariato concettualmente)
567// ===========================================================================
568static const char* kKsTable = "bastionguard_ks";
569
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}
607
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}
617
619 return run({"nft", "list", "table", "inet", kKsTable}, true).ok;
620}
621
622} // namespace BastionGuard
#define AF_INET
Definition bsc_ebpf.c:56
#define AF_INET6
Definition bsc_ebpf.c:57
OpResult run(const std::vector< std::string > &argv, bool privileged) const
bool load_from_conf(const std::string &conf_path, std::string *err=nullptr)
WireguardBackend(std::string iface="wg0")
OpResult run_with_stdin(const std::vector< std::string > &argv, const std::string &stdin_data, bool privileged) const
bool load_from_params(const WgConfigParams &p, std::string *err=nullptr)
bool tooling_available(std::string *missing=nullptr) const
std::optional< WgKeyPair > generate_keys() const
std::optional< std::string > public_ip() const
std::string build_wg_setconf_text() const
static bool valid_ip_(const std::string &s)
static bool valid_cidr_(const std::string &s)
static bool validate_spec_(const WgTunnelSpec &sp, std::string *err)
static const char * kKsTable
static std::string strip_(std::string s)
static std::vector< std::string > split_csv_(const std::string &s)
static bool valid_wg_key_(const std::string &s)
static bool parse_uint_range_(const std::string &s, unsigned min, unsigned max)
static bool valid_iface_(const std::string &iface)
static bool valid_hostname_(const std::string &host)
static bool in_path_(const char *bin)
static bool needs_default_route_(const WgTunnelSpec &sp)
static bool valid_endpoint_(const std::string &endpoint)
std::vector< WgPeer > peers
std::vector< std::string > dns
std::vector< std::string > addresses