pull/1688/head
Jason Rhinelander 3 years ago committed by Jeff Becker
parent 9dd604820f
commit e11efe9bc5
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -25,19 +25,23 @@ extern NSString* error_domain;
uv_udp_t request_socket;
// The reply address. This is a bit hacky: we configure libunbound to just use single address
// (rather than a range) so that we don't have to worry about tracking different reply addresses.
@public struct sockaddr reply_addr;
@public
struct sockaddr reply_addr;
// UDP "session" aimed at the upstream DNS
@public NWUDPSession* upstream;
@public
NWUDPSession* upstream;
// Apple docs say writes could take time *and* the crappy Apple datagram write methods aren't
// callable again until the previous write finishes. Deal with this garbage API by queuing
// everything than using a uv_async to process the queue.
@public int write_ready;
@public NSMutableArray<NSData*>* pending_writes;
@public
int write_ready;
@public
NSMutableArray<NSData*>* pending_writes;
uv_async_t write_trigger;
}
- (void)startWithUpstreamDns:(NWUDPSession*) dns
listenPort:(uint16_t) listenPort
uvLoop:(uv_loop_t*) loop
- (void)startWithUpstreamDns:(NWUDPSession*)dns
listenPort:(uint16_t)listenPort
uvLoop:(uv_loop_t*)loop
completionHandler:(void (^)(NSError* error))completionHandler;
- (void)flushWrites;
@ -45,4 +49,3 @@ extern NSString* error_domain;
- (void)dealloc;
@end

@ -11,7 +11,8 @@ namespace llarp::apple
std::shared_ptr<vpn::Platform>
makeVPNPlatform() override
{
return std::make_shared<VPNPlatform>(*this, m_PacketWriter, m_OnReadable, route_callbacks, callback_context);
return std::make_shared<VPNPlatform>(
*this, m_PacketWriter, m_OnReadable, route_callbacks, callback_context);
}
// Callbacks that must be set for packet handling *before* calling Setup/Configure/Run; the main

@ -34,7 +34,8 @@ const uint16_t dns_trampoline_port = 1053;
void*
llarp_apple_init(llarp_apple_config* appleconf)
{
llarp::LogContext::Instance().logStream = std::make_unique<llarp::apple::NSLogStream>(appleconf->ns_logger);
llarp::LogContext::Instance().logStream =
std::make_unique<llarp::apple::NSLogStream>(appleconf->ns_logger);
try
{
@ -42,7 +43,7 @@ llarp_apple_init(llarp_apple_config* appleconf)
auto config = std::make_shared<llarp::Config>(config_dir);
fs::path config_path = config_dir / "lokinet.ini";
if (!fs::exists(config_path))
llarp::ensureConfig(config_dir, config_path, /*overwrite=*/ false, /*router=*/ false);
llarp::ensureConfig(config_dir, config_path, /*overwrite=*/false, /*router=*/false);
config->Load(config_path);
// If no range is specified then go look for a free one, set that in the config, and then return
@ -63,8 +64,10 @@ llarp_apple_init(llarp_apple_config* appleconf)
std::strcpy(appleconf->tunnel_ipv4_netmask, mask.c_str());
appleconf->upstream_dns[0] = '\0';
for (auto& upstream : config->dns.m_upstreamDNS) {
if (upstream.isIPv4()) {
for (auto& upstream : config->dns.m_upstreamDNS)
{
if (upstream.isIPv4())
{
std::strcpy(appleconf->upstream_dns, upstream.hostString().c_str());
appleconf->upstream_dns_port = upstream.getPort();
break;
@ -87,7 +90,6 @@ llarp_apple_init(llarp_apple_config* appleconf)
inst->packet_writer = appleconf->packet_writer;
inst->start_reading = appleconf->start_reading;
return inst.release();
}
catch (const std::exception& e)
@ -98,25 +100,21 @@ llarp_apple_init(llarp_apple_config* appleconf)
}
int
llarp_apple_start(
void* lokinet,
void* callback_context)
llarp_apple_start(void* lokinet, void* callback_context)
{
auto* inst = static_cast<instance_data*>(lokinet);
inst->context.callback_context = callback_context;
inst->context.m_PacketWriter = [inst, callback_context](
int af_family, void* data, size_t size) {
inst->packet_writer(af_family, data, size, callback_context);
return true;
inst->context.m_PacketWriter = [inst, callback_context](int af_family, void* data, size_t size) {
inst->packet_writer(af_family, data, size, callback_context);
return true;
};
inst->context.m_OnReadable =
[inst, callback_context](llarp::apple::VPNInterface& iface) {
inst->iface = iface.weak_from_this();
inst->start_reading(callback_context);
};
inst->context.m_OnReadable = [inst, callback_context](llarp::apple::VPNInterface& iface) {
inst->iface = iface.weak_from_this();
inst->start_reading(callback_context);
};
std::promise<void> result;
inst->runner = std::thread{[inst, &result] {

@ -17,91 +17,91 @@ extern "C"
extern const uint16_t dns_trampoline_port;
/// C callback function for us to invoke when we need to write a packet
typedef void(*packet_writer_callback)(int af, const void* data, size_t size, void* ctx);
typedef void (*packet_writer_callback)(int af, const void* data, size_t size, void* ctx);
/// C callback function to invoke once we are ready to start receiving packets
typedef void(*start_reading_callback)(void* ctx);
typedef void (*start_reading_callback)(void* ctx);
/// C callback that bridges things into NSLog
typedef void(*ns_logger_callback)(const char* msg);
typedef void (*ns_logger_callback)(const char* msg);
/// C callbacks to add/remove specific and default routes to the tunnel
typedef void(*llarp_route_ipv4_callback)(const char* addr, const char* netmask, void* ctx);
typedef void(*llarp_route_ipv6_callback)(const char* addr, int prefix, void* ctx);
typedef void(*llarp_default_route_callback)(void* ctx);
typedef struct llarp_route_callbacks {
/// Callback invoked to set up an IPv4 range that should be routed through the tunnel
/// interface. Called with the address and netmask.
llarp_route_ipv4_callback add_ipv4_route;
/// Callback invoked to set the tunnel as the default IPv4 route.
llarp_default_route_callback add_ipv4_default_route;
/// Callback invoked to remove a specific range from the tunnel IPv4 routes. Called with the
/// address and netmask.
llarp_route_ipv4_callback del_ipv4_route;
/// Callback invoked to set up an IPv6 range that should be routed through the tunnel
/// interface. Called with the address and netmask.
llarp_route_ipv6_callback add_ipv6_route;
/// Callback invoked to remove a specific range from the tunnel IPv6 routes. Called with the
/// address and netmask.
llarp_route_ipv6_callback del_ipv6_route;
/// Callback invoked to set the tunnel as the default IPv4/IPv6 route.
llarp_default_route_callback add_default_route;
/// Callback invoked to remove the tunnel as the default IPv4/IPv6 route.
llarp_default_route_callback del_default_route;
typedef void (*llarp_route_ipv4_callback)(const char* addr, const char* netmask, void* ctx);
typedef void (*llarp_route_ipv6_callback)(const char* addr, int prefix, void* ctx);
typedef void (*llarp_default_route_callback)(void* ctx);
typedef struct llarp_route_callbacks
{
/// Callback invoked to set up an IPv4 range that should be routed through the tunnel
/// interface. Called with the address and netmask.
llarp_route_ipv4_callback add_ipv4_route;
/// Callback invoked to set the tunnel as the default IPv4 route.
llarp_default_route_callback add_ipv4_default_route;
/// Callback invoked to remove a specific range from the tunnel IPv4 routes. Called with the
/// address and netmask.
llarp_route_ipv4_callback del_ipv4_route;
/// Callback invoked to set up an IPv6 range that should be routed through the tunnel
/// interface. Called with the address and netmask.
llarp_route_ipv6_callback add_ipv6_route;
/// Callback invoked to remove a specific range from the tunnel IPv6 routes. Called with the
/// address and netmask.
llarp_route_ipv6_callback del_ipv6_route;
/// Callback invoked to set the tunnel as the default IPv4/IPv6 route.
llarp_default_route_callback add_default_route;
/// Callback invoked to remove the tunnel as the default IPv4/IPv6 route.
llarp_default_route_callback del_default_route;
} llarp_route_callbacks;
/// Pack of crap to be passed into llarp_apple_init to initialize
typedef struct llarp_apple_config
{
/// lokinet configuration directory, expected to be the application-specific "home" directory,
/// which is where state files are stored and the lokinet.ini will be loaded (or created if it
/// doesn't exist).
const char* config_dir;
/// path to the default bootstrap.signed file included in installation, which will be used by
/// default when no specific bootstrap is in the config file.
const char* default_bootstrap;
/// llarp_apple_init writes the IP address for the primary tunnel IP address here,
/// null-terminated.
char tunnel_ipv4_ip[16];
/// llarp_apple_init writes the netmask of the tunnel address here, null-terminated.
char tunnel_ipv4_netmask[16];
/// The first upstream DNS server's IPv4 address the OS should use when in exit mode.
/// (Currently on mac in exit mode we only support querying the first such configured server).
char upstream_dns[16];
uint16_t upstream_dns_port;
/// \defgroup callbacks Callbacks
/// Callbacks we invoke for various operations that require glue into the Apple network
/// extension APIs. All of these except for ns_logger are passed the pointer provided to
/// llarp_apple_start when invoked.
/// @{
/// simple wrapper around NSLog for lokinet message logging
ns_logger_callback ns_logger;
/// C function callback that will be called when we need to write a packet to the packet
/// tunnel. Will be passed AF_INET or AF_INET6, a void pointer to the data, and the size of
/// the data in bytes.
packet_writer_callback packet_writer;
/// C function callback that will be called when lokinet is setup and ready to start receiving
/// packets from the packet tunnel. This should set up the read handler to deliver packets
/// via llarp_apple_incoming.
start_reading_callback start_reading;
/// Callbacks invoked to add/remove routes to the tunnel.
llarp_route_callbacks route_callbacks;
/// @}
/// lokinet configuration directory, expected to be the application-specific "home" directory,
/// which is where state files are stored and the lokinet.ini will be loaded (or created if it
/// doesn't exist).
const char* config_dir;
/// path to the default bootstrap.signed file included in installation, which will be used by
/// default when no specific bootstrap is in the config file.
const char* default_bootstrap;
/// llarp_apple_init writes the IP address for the primary tunnel IP address here,
/// null-terminated.
char tunnel_ipv4_ip[16];
/// llarp_apple_init writes the netmask of the tunnel address here, null-terminated.
char tunnel_ipv4_netmask[16];
/// The first upstream DNS server's IPv4 address the OS should use when in exit mode.
/// (Currently on mac in exit mode we only support querying the first such configured server).
char upstream_dns[16];
uint16_t upstream_dns_port;
/// \defgroup callbacks Callbacks
/// Callbacks we invoke for various operations that require glue into the Apple network
/// extension APIs. All of these except for ns_logger are passed the pointer provided to
/// llarp_apple_start when invoked.
/// @{
/// simple wrapper around NSLog for lokinet message logging
ns_logger_callback ns_logger;
/// C function callback that will be called when we need to write a packet to the packet
/// tunnel. Will be passed AF_INET or AF_INET6, a void pointer to the data, and the size of
/// the data in bytes.
packet_writer_callback packet_writer;
/// C function callback that will be called when lokinet is setup and ready to start receiving
/// packets from the packet tunnel. This should set up the read handler to deliver packets
/// via llarp_apple_incoming.
start_reading_callback start_reading;
/// Callbacks invoked to add/remove routes to the tunnel.
llarp_route_callbacks route_callbacks;
/// @}
} llarp_apple_config;
/// Initializes a lokinet instance by initializing various objects and loading the configuration
/// (if <config_dir>/lokinet.ini exists). Does not actually start lokinet (call llarp_apple_start
/// for that).

@ -4,86 +4,98 @@
#include <llarp.hpp>
#include <memory>
namespace llarp::apple {
void RouteManager::check_trampoline(bool enable) {
if (trampoline_active == enable)
return;
auto router = context.router;
if (!router) {
LogError("Cannot reconfigure to use DNS trampoline: no router");
return;
}
namespace llarp::apple
{
void
RouteManager::check_trampoline(bool enable)
{
if (trampoline_active == enable)
return;
auto router = context.router;
if (!router)
{
LogError("Cannot reconfigure to use DNS trampoline: no router");
return;
}
std::shared_ptr<llarp::handlers::TunEndpoint> tun;
router->hiddenServiceContext().ForEachService([&tun] (const auto& name, const auto ep) {
std::shared_ptr<llarp::handlers::TunEndpoint> tun;
router->hiddenServiceContext().ForEachService([&tun](const auto& name, const auto ep) {
tun = std::dynamic_pointer_cast<llarp::handlers::TunEndpoint>(ep);
return !tun;
});
});
if (!tun) {
LogError("Cannot reconfigure to use DNS trampoline: no tun endpoint found (!?)");
return;
}
if (enable)
saved_upstream_dns = tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, huint16_t{dns_trampoline_port}}});
else
tun->ReconfigureDNS(std::move(saved_upstream_dns));
trampoline_active = enable;
}
if (!tun)
{
LogError("Cannot reconfigure to use DNS trampoline: no tun endpoint found (!?)");
return;
}
if (enable)
saved_upstream_dns =
tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, huint16_t{dns_trampoline_port}}});
else
tun->ReconfigureDNS(std::move(saved_upstream_dns));
trampoline_active = enable;
}
void RouteManager::AddDefaultRouteViaInterface(std::string)
{
check_trampoline(true);
void RouteManager::AddDefaultRouteViaInterface(std::string)
{
check_trampoline(true);
if (callback_context and route_callbacks.add_default_route)
route_callbacks.add_default_route(callback_context);
}
route_callbacks.add_default_route(callback_context);
}
void RouteManager::DelDefaultRouteViaInterface(std::string)
{
check_trampoline(false);
void RouteManager::DelDefaultRouteViaInterface(std::string)
{
check_trampoline(false);
if (callback_context and route_callbacks.del_default_route)
route_callbacks.del_default_route(callback_context);
}
route_callbacks.del_default_route(callback_context);
}
void
RouteManager::AddRouteViaInterface(vpn::NetworkInterface&, IPRange range)
{
check_trampoline(true);
void
RouteManager::AddRouteViaInterface(vpn::NetworkInterface&, IPRange range)
{
check_trampoline(true);
if (callback_context)
{
if (range.IsV4()) {
if (route_callbacks.add_ipv4_route)
route_callbacks.add_ipv4_route(
range.BaseAddressString().c_str(),
net::TruncateV6(range.netmask_bits).ToString().c_str(),
callback_context);
} else {
if (route_callbacks.add_ipv6_route)
route_callbacks.add_ipv6_route(range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context);
}
if (range.IsV4())
{
if (route_callbacks.add_ipv4_route)
route_callbacks.add_ipv4_route(
range.BaseAddressString().c_str(),
net::TruncateV6(range.netmask_bits).ToString().c_str(),
callback_context);
}
else
{
if (route_callbacks.add_ipv6_route)
route_callbacks.add_ipv6_route(
range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context);
}
}
}
}
void
RouteManager::DelRouteViaInterface(vpn::NetworkInterface&, IPRange range)
{
check_trampoline(false);
void
RouteManager::DelRouteViaInterface(vpn::NetworkInterface&, IPRange range)
{
check_trampoline(false);
if (callback_context)
{
if (range.IsV4()) {
if (route_callbacks.del_ipv4_route)
route_callbacks.del_ipv4_route(
range.BaseAddressString().c_str(),
net::TruncateV6(range.netmask_bits).ToString().c_str(),
callback_context);
} else {
if (route_callbacks.del_ipv6_route)
route_callbacks.del_ipv6_route(range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context);
}
if (range.IsV4())
{
if (route_callbacks.del_ipv4_route)
route_callbacks.del_ipv4_route(
range.BaseAddressString().c_str(),
net::TruncateV6(range.netmask_bits).ToString().c_str(),
callback_context);
}
else
{
if (route_callbacks.del_ipv6_route)
route_callbacks.del_ipv6_route(
range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context);
}
}
}
}
}
} // namespace llarp::apple

@ -4,20 +4,24 @@
#include <llarp/ev/vpn.hpp>
#include "context_wrapper.h"
namespace llarp::apple {
class RouteManager final : public llarp::vpn::IRouteManager {
public:
namespace llarp::apple
{
class RouteManager final : public llarp::vpn::IRouteManager
{
public:
RouteManager(llarp::Context& ctx, llarp_route_callbacks rcs, void* callback_context)
: context{ctx}, route_callbacks{std::move(rcs)}, callback_context{callback_context} {}
: context{ctx}, route_callbacks{std::move(rcs)}, callback_context{callback_context}
{}
/// These are called for poking route holes, but we don't have to do that at all on macos
/// because the appex isn't subject to its own rules.
void
AddRoute(IPVariant_t ip, IPVariant_t gateway) override {}
AddRoute(IPVariant_t ip, IPVariant_t gateway) override
{}
void
DelRoute(IPVariant_t ip, IPVariant_t gateway) override {}
DelRoute(IPVariant_t ip, IPVariant_t gateway) override
{}
void
AddDefaultRouteViaInterface(std::string ifname) override;
@ -32,23 +36,24 @@ public:
DelRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override;
virtual std::vector<IPVariant_t>
GetGatewaysNotOnInterface(std::string ifname) override {
// We can't get this on mac from our sandbox, but we don't actually need it because we
// ignore the gateway for AddRoute/DelRoute anyway, so just return a zero IP.
std::vector<IPVariant_t> ret;
ret.push_back(huint32_t{0});
return ret;
GetGatewaysNotOnInterface(std::string ifname) override
{
// We can't get this on mac from our sandbox, but we don't actually need it because we
// ignore the gateway for AddRoute/DelRoute anyway, so just return a zero IP.
std::vector<IPVariant_t> ret;
ret.push_back(huint32_t{0});
return ret;
}
private:
private:
llarp::Context& context;
bool trampoline_active = false;
std::vector<llarp::SockAddr> saved_upstream_dns;
void check_trampoline(bool enable);
void
check_trampoline(bool enable);
void* callback_context = nullptr;
llarp_route_callbacks route_callbacks;
};
};
}
} // namespace llarp::apple

@ -18,7 +18,11 @@ namespace llarp::apple
std::shared_ptr<vpn::NetworkInterface> ObtainInterface(vpn::InterfaceInfo) override;
vpn::IRouteManager& RouteManager() override { return m_RouteManager; }
vpn::IRouteManager&
RouteManager() override
{
return m_RouteManager;
}
private:
Context& m_Context;

@ -103,7 +103,9 @@ namespace llarp::dns
llarp::LogError("dns reply failed");
}
bool PacketHandler::IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const {
bool
PacketHandler::IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const
{
return m_Resolvers.count(to);
}

@ -58,7 +58,8 @@ namespace llarp
// Returns true if this packet is something that looks like it's going to an upstream
// resolver, i.e. matches a configured resolver.
virtual bool IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const;
virtual bool
IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const;
private:
void

@ -157,16 +157,17 @@ namespace llarp::dns
// On Apple, we configure a localhost resolver to trampoline requests through the tunnel to the
// actual upstream (because the network extension itself cannot route through the tunnel using
// normal sockets but instead we "get" to use Apple's interfaces, hurray).
if (hoststr == "127.0.0.1") {
// Not at all clear why this is needed but without it we get "send failed: Can't assign
// requested address" when unbound tries to connect to the localhost address using a source
// address of 0.0.0.0. Yay apple.
ub_ctx_set_option(unboundContext, "outgoing-interface:", hoststr.c_str());
// The trampoline expects just a single source port (and sends everything back to it)
ub_ctx_set_option(unboundContext, "outgoing-range:", "1");
ub_ctx_set_option(unboundContext, "outgoing-port-avoid:", "0-65535");
ub_ctx_set_option(unboundContext, "outgoing-port-permit:", "1253");
if (hoststr == "127.0.0.1")
{
// Not at all clear why this is needed but without it we get "send failed: Can't assign
// requested address" when unbound tries to connect to the localhost address using a source
// address of 0.0.0.0. Yay apple.
ub_ctx_set_option(unboundContext, "outgoing-interface:", hoststr.c_str());
// The trampoline expects just a single source port (and sends everything back to it)
ub_ctx_set_option(unboundContext, "outgoing-range:", "1");
ub_ctx_set_option(unboundContext, "outgoing-port-avoid:", "0-65535");
ub_ctx_set_option(unboundContext, "outgoing-port-permit:", "1253");
}
#endif

@ -71,8 +71,9 @@ namespace llarp
bool
IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const override
{
LogError("IsUpstreamResolver? ", to.asIPv6(), " != ", m_Endpoint->GetIfAddr(), ", from=", from);
return to.asIPv6() != m_Endpoint->GetIfAddr();
LogError(
"IsUpstreamResolver? ", to.asIPv6(), " != ", m_Endpoint->GetIfAddr(), ", from=", from);
return to.asIPv6() != m_Endpoint->GetIfAddr();
}
#endif
};
@ -153,12 +154,12 @@ namespace llarp
std::vector<SockAddr>
TunEndpoint::ReconfigureDNS(std::vector<SockAddr> servers)
{
std::swap(m_UpstreamResolvers, servers);
m_Resolver->Stop();
if (!m_Resolver->Start(
m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
llarp::LogError(Name(), " failed to reconfigure DNS server");
return servers;
std::swap(m_UpstreamResolvers, servers);
m_Resolver->Stop();
if (!m_Resolver->Start(
m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
llarp::LogError(Name(), " failed to reconfigure DNS server");
return servers;
}
bool

Loading…
Cancel
Save