Wires up systemd support to configure DNS on startup and when enabling/disabling exit mode. On startup (and when turning off an exit) we tell systemd-resolved to direct .loki and .snode lookups to lokinet (leaving other DNS traffic alone). On exit enabling, we reconfigure it to resolve "." (i.e. the root DNS domain) so that all lookups come into it.dev
parent
35e4e8817b
commit
4ef25ef679
@ -1,3 +0,0 @@
|
||||
[Resolve]
|
||||
DNS=127.3.2.1
|
||||
Domains=~loki ~snode
|
@ -0,0 +1,4 @@
|
||||
[Allow lokinet to set DNS settings]
|
||||
Identity=unix-user:_lokinet
|
||||
Action=org.freedesktop.resolve1.set-dns-servers;org.freedesktop.resolve1.set-domains
|
||||
ResultAny=yes
|
@ -0,0 +1,9 @@
|
||||
/* Allow lokinet to set DNS settings */
|
||||
polkit.addRule(function(action, subject) {
|
||||
if ((action.id == "org.freedesktop.resolve1.set-dns-servers" ||
|
||||
action.id == "org.freedesktop.resolve1.set-domains") &&
|
||||
subject.user == "_lokinet") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,129 @@
|
||||
#include "systemd_resolved.hpp"
|
||||
#include <llarp/util/logging/logger.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" {
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <net/if.h>
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace llarp {
|
||||
|
||||
#ifndef WITH_SYSTEMD
|
||||
|
||||
bool systemd_resolved_set_dns(std::string, llarp::SockAddr, bool) {
|
||||
LogDebug("lokinet is not build with systemd support, cannot set systemd resolved DNS");
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace {
|
||||
template <typename... T>
|
||||
void resolved_call(sd_bus* bus, const char* method, const char* arg_format, T... args) {
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus_message *msg = nullptr;
|
||||
int r = sd_bus_call_method(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
method,
|
||||
&error,
|
||||
&msg,
|
||||
arg_format,
|
||||
args...);
|
||||
|
||||
if (r < 0)
|
||||
throw std::runtime_error{"sdbus resolved "s + method + " failed: " + strerror(-r)};
|
||||
|
||||
sd_bus_message_unref(msg);
|
||||
sd_bus_error_free(&error);
|
||||
}
|
||||
|
||||
struct sd_bus_deleter { void operator()(sd_bus* ptr) const { sd_bus_unref(ptr); } };
|
||||
}
|
||||
|
||||
bool systemd_resolved_set_dns(std::string ifname, llarp::SockAddr dns, bool global) {
|
||||
unsigned int if_ndx = if_nametoindex(ifname.c_str());
|
||||
if (if_ndx == 0) {
|
||||
LogWarn("No such interface '", ifname, "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to the system bus
|
||||
sd_bus *bus = nullptr;
|
||||
int r = sd_bus_open_system(&bus);
|
||||
if (r < 0) {
|
||||
LogWarn("Failed to connect to system bus to set DNS: ", strerror(-r));
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<sd_bus, sd_bus_deleter> bus_ptr{bus};
|
||||
|
||||
try {
|
||||
// This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but the
|
||||
// alternative is to build up a bunch of crap with va_args, which is slightly more gross.
|
||||
if (dns.isIPv6()) {
|
||||
auto ipv6 = dns.getIPv6();
|
||||
static_assert(sizeof(ipv6) == 16);
|
||||
auto* a = reinterpret_cast<const uint8_t*>(&ipv6);
|
||||
resolved_call(bus, "SetLinkDNSEx", "ia(iayqs)",
|
||||
(int32_t) if_ndx,
|
||||
(int) 1, // number of "iayqs"s we are passing
|
||||
(int32_t) AF_INET6, // network address type
|
||||
(int) 16, // network addr byte size
|
||||
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], // yuck
|
||||
(uint16_t) dns.getPort(),
|
||||
nullptr // dns server name (for TLS SNI which we don't care about)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ipv4 = dns.getIPv4();
|
||||
static_assert(sizeof(ipv4) == 4);
|
||||
auto* a = reinterpret_cast<const uint8_t*>(&ipv4);
|
||||
resolved_call(bus, "SetLinkDNSEx", "ia(iayqs)",
|
||||
(int32_t) if_ndx,
|
||||
(int) 1, // number of "iayqs"s we are passing
|
||||
(int32_t) AF_INET, // network address type
|
||||
(int) 4, // network addr byte size
|
||||
a[0], a[1], a[2], a[3], // yuck
|
||||
(uint16_t) dns.getPort(),
|
||||
nullptr // dns server name (for TLS SNI which we don't care about)
|
||||
);
|
||||
}
|
||||
|
||||
if (global)
|
||||
// Setting "." as a routing domain gives this DNS server higher priority in resolution
|
||||
// compared to dns servers that are set without a domain (e.g. the default for a
|
||||
// DHCP-configured DNS server)
|
||||
resolved_call(bus, "SetLinkDomains", "ia(sb)",
|
||||
(int32_t) if_ndx,
|
||||
(int) 1, // array size
|
||||
"." // global DNS root
|
||||
);
|
||||
else
|
||||
// Only resolve .loki and .snode through lokinet (so you keep using your local DNS server for
|
||||
// everything else, which is nicer than forcing everything though lokinet's upstream DNS).
|
||||
resolved_call(bus, "SetLinkDomains", "ia(sb)",
|
||||
(int32_t) if_ndx,
|
||||
(int) 2, // array size
|
||||
"loki", // domain
|
||||
(int) 1, // routing domain = true
|
||||
"snode", // domain
|
||||
(int) 1 // routing domain = true
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LogWarn("Failed to set DNS via systemd-resolved: ", e.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // WITH_SYSTEMD
|
||||
|
||||
} // namespace llarp
|
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <llarp/net/sock_addr.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/// Attempts to set lokinet as the DNS server for systemd-resolved. Returns true if successful,
|
||||
/// false if unsupported or fails. (When compiled without systemd support this always returns
|
||||
/// false without doing anything).
|
||||
///
|
||||
/// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0.
|
||||
/// Typically tun_endpoint.GetIfName().
|
||||
/// \param dns -- the listening address of the lokinet DNS server
|
||||
/// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
|
||||
/// addresses (false).
|
||||
bool systemd_resolved_set_dns(std::string if_name, llarp::SockAddr dns, bool global);
|
||||
}
|
Loading…
Reference in new issue