diff --git a/llarp/apple/CMakeLists.txt b/llarp/apple/CMakeLists.txt index 8344e90a1..69a356020 100644 --- a/llarp/apple/CMakeLists.txt +++ b/llarp/apple/CMakeLists.txt @@ -15,7 +15,7 @@ find_library(COREFOUNDATION CoreFoundation REQUIRED) target_sources(lokinet-util PRIVATE apple_logger.cpp) target_link_libraries(lokinet-util PUBLIC ${FOUNDATION}) -target_sources(lokinet-platform PRIVATE vpn_interface.cpp context_wrapper.cpp) +target_sources(lokinet-platform PRIVATE vpn_interface.cpp route_manager.cpp context_wrapper.cpp) add_executable(lokinet-extension MACOSX_BUNDLE PacketTunnelProvider.m diff --git a/llarp/apple/PacketTunnelProvider.m b/llarp/apple/PacketTunnelProvider.m index 1255f61c1..56ef0ca22 100644 --- a/llarp/apple/PacketTunnelProvider.m +++ b/llarp/apple/PacketTunnelProvider.m @@ -7,6 +7,8 @@ NSString* error_domain = @"com.loki-project.lokinet"; @interface LLARPPacketTunnel : NEPacketTunnelProvider { void* lokinet; + @public NEPacketTunnelNetworkSettings* settings; + @public NEIPv4Route* tun_route4; } - (void)startTunnelWithOptions:(NSDictionary*)options @@ -20,6 +22,8 @@ NSString* error_domain = @"com.loki-project.lokinet"; - (void)readPackets; +- (void)updateNetworkSettings; + @end void nslogger(const char* msg) { NSLog(@"%s", msg); } @@ -42,6 +46,97 @@ void start_packet_reader(void* ctx) { [t readPackets]; } +void add_ipv4_route(const char* addr, const char* netmask, void* ctx) { + NEIPv4Route* route = [[NEIPv4Route alloc] + initWithDestinationAddress: [NSString stringWithUTF8String:addr] + subnetMask: [NSString stringWithUTF8String:netmask]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + for (NEIPv4Route* r in t->settings.IPv4Settings.includedRoutes) + if ([r.destinationAddress isEqualToString:route.destinationAddress] && + [r.destinationSubnetMask isEqualToString:route.destinationSubnetMask]) + return; // Already in the settings, nothing to add. + + t->settings.IPv4Settings.includedRoutes = + [t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route]; + + [t updateNetworkSettings]; +} + +void del_ipv4_route(const char* addr, const char* netmask, void* ctx) { + NEIPv4Route* route = [[NEIPv4Route alloc] + initWithDestinationAddress: [NSString stringWithUTF8String:addr] + subnetMask: [NSString stringWithUTF8String:netmask]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + NSMutableArray* routes = [NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes]; + for (int i = 0; i < routes.count; i++) { + if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && + [routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask]) { + [routes removeObjectAtIndex:i]; + i--; + } + } + + if (routes.count != t->settings.IPv4Settings.includedRoutes.count) { + t->settings.IPv4Settings.includedRoutes = routes; + [t updateNetworkSettings]; + } +} + +void add_ipv6_route(const char* addr, int prefix, void* ctx) { + NEIPv6Route* route = [[NEIPv6Route alloc] + initWithDestinationAddress: [NSString stringWithUTF8String:addr] + networkPrefixLength: [NSNumber numberWithInt:prefix]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + for (NEIPv6Route* r in t->settings.IPv6Settings.includedRoutes) + if ([r.destinationAddress isEqualToString:route.destinationAddress] && + [r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) + return; // Already in the settings, nothing to add. + + t->settings.IPv6Settings.includedRoutes = + [t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route]; + + [t updateNetworkSettings]; +} +void del_ipv6_route(const char* addr, int prefix, void* ctx) { + NEIPv6Route* route = [[NEIPv6Route alloc] + initWithDestinationAddress: [NSString stringWithUTF8String:addr] + networkPrefixLength: [NSNumber numberWithInt:prefix]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + NSMutableArray* routes = [NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes]; + for (int i = 0; i < routes.count; i++) { + if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && + [routes[i].destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) { + [routes removeObjectAtIndex:i]; + i--; + } + } + + if (routes.count != t->settings.IPv6Settings.includedRoutes.count) { + t->settings.IPv6Settings.includedRoutes = routes; + [t updateNetworkSettings]; + } +} +void add_default_route(void* ctx) { + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + + t->settings.IPv4Settings.includedRoutes = @[NEIPv4Route.defaultRoute]; + t->settings.IPv6Settings.includedRoutes = @[NEIPv6Route.defaultRoute]; + + [t updateNetworkSettings]; +} +void del_default_route(void* ctx) { + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx; + + t->settings.IPv4Settings.includedRoutes = @[t->tun_route4]; + t->settings.IPv4Settings.includedRoutes = @[]; // No tun_route6 yet. + + [t updateNetworkSettings]; +} + @implementation LLARPPacketTunnel - (void)readPackets @@ -63,21 +158,39 @@ void start_packet_reader(void* ctx) { char mask_buf[16]; char dns_buf[16]; - NSString* default_bootstrap = [[NSBundle mainBundle] pathForResource:@"bootstrap" ofType:@"signed"]; + NSString* default_bootstrap = [NSBundle.mainBundle pathForResource:@"bootstrap" ofType:@"signed"]; + NSString* home = NSHomeDirectory(); - lokinet = llarp_apple_init(nslogger, NSHomeDirectory().UTF8String, default_bootstrap.UTF8String, ip_buf, mask_buf, dns_buf); + llarp_apple_config conf = { + .config_dir = home.UTF8String, + .default_bootstrap = default_bootstrap.UTF8String, + .ns_logger = nslogger, + .packet_writer = packet_writer, + .start_reading = start_packet_reader, + .route_callbacks = { + .add_ipv4_route = add_ipv4_route, + .del_ipv4_route = del_ipv4_route, + .add_ipv6_route = add_ipv6_route, + .del_ipv6_route = del_ipv6_route, + .add_default_route = add_default_route, + .del_default_route = del_default_route + }, + }; + + lokinet = llarp_apple_init(&conf); if (!lokinet) { NSError *init_failure = [NSError errorWithDomain:error_domain code:500 userInfo:@{@"Error": @"Failed to initialize lokinet"}]; NSLog(@"%@", [init_failure localizedDescription]); return completionHandler(init_failure); } - NSString* ip = [[NSString alloc] initWithUTF8String:ip_buf]; - NSString* mask = [[NSString alloc] initWithUTF8String:mask_buf]; - NSString* dnsaddr = [[NSString alloc] initWithUTF8String:dns_buf]; - - NEPacketTunnelNetworkSettings* settings = - [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:@"127.0.0.1"]; + NSString* ip = [NSString stringWithUTF8String:conf.tunnel_ipv4_ip]; + NSString* mask = [NSString stringWithUTF8String:conf.tunnel_ipv4_netmask]; + NSString* dnsaddr = [NSString stringWithUTF8String:conf.tunnel_dns]; + + // We don't have a fixed address so just stick some bogus value here: + settings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:@"127.3.2.1"]; + NEDNSSettings* dns = [[NEDNSSettings alloc] initWithServers:@[dnsaddr]]; dns.domainName = @"localhost.loki"; // In theory, matchDomains is supposed to be set to DNS suffixes that we resolve. This seems @@ -96,16 +209,21 @@ void start_packet_reader(void* ctx) { dns.searchDomains = @[]; NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip] subnetMasks:@[mask]]; - ipv4.includedRoutes = @[[[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask: mask]]; + tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask: mask]; + ipv4.includedRoutes = @[tun_route4]; settings.IPv4Settings = ipv4; settings.DNSSettings = dns; + __weak LLARPPacketTunnel* weakSelf = self; [self setTunnelNetworkSettings:settings completionHandler:^(NSError* err) { if (err) { NSLog(@"Failed to configure lokinet tunnel: %@", err); return completionHandler(err); } - - int start_ret = llarp_apple_start(lokinet, packet_writer, start_packet_reader, (__bridge void*) self); + LLARPPacketTunnel* strongSelf = weakSelf; + if (!strongSelf) + return completionHandler(nil); + + int start_ret = llarp_apple_start(strongSelf->lokinet, (__bridge void*) strongSelf); if (start_ret != 0) { NSError *start_failure = [NSError errorWithDomain:error_domain code:start_ret userInfo:@{@"Error": @"Failed to start lokinet"}]; NSLog(@"%@", start_failure); @@ -132,4 +250,34 @@ void start_packet_reader(void* ctx) { NSData* response = [NSData dataWithBytesNoCopy:"ok" length:3 freeWhenDone:NO]; completionHandler(response); } + +- (void)updateNetworkSettings +{ + self.reasserting = YES; + __weak LLARPPacketTunnel* weakSelf = self; + // Apple documentation says that setting network settings to nil isn't required before setting it + // to a new value. Apple lies: both end up with a routing table that looks exactly the same (from + // both `netstat -rn` and from everything that happens in `route -n monitor`), but if we don't + // call with nil first then everything fails to route to either lokinet *and* clearnet through the + // exit, so there is apparently some special magic internal Apple state that actually *does* + // require the tunnel settings being reset with nil first. + // + // Thanks for the accurate documentation, Apple. + // + [self setTunnelNetworkSettings:nil completionHandler:^(NSError* err) { + if (err) + NSLog(@"Failed to clear lokinet tunnel settings: %@", err); + LLARPPacketTunnel* strongSelf = weakSelf; + if (strongSelf) { + [weakSelf setTunnelNetworkSettings:strongSelf->settings completionHandler:^(NSError* err) { + LLARPPacketTunnel* strongSelf = weakSelf; + if (strongSelf) + strongSelf.reasserting = NO; + if (err) + NSLog(@"Failed to reconfigure lokinet tunnel settings: %@", err); + }]; + } + }]; +} + @end diff --git a/llarp/apple/context.hpp b/llarp/apple/context.hpp index e25090262..a3b92ad75 100644 --- a/llarp/apple/context.hpp +++ b/llarp/apple/context.hpp @@ -2,6 +2,7 @@ #include #include "vpn_platform.hpp" +#include "route_manager.hpp" namespace llarp::apple { @@ -10,7 +11,7 @@ namespace llarp::apple std::shared_ptr makeVPNPlatform() override { - return std::make_shared(*this, m_PacketWriter, m_OnReadable); + return std::make_shared(*this, m_PacketWriter, m_OnReadable, route_callbacks, callback_context); } // Callbacks that must be set for packet handling *before* calling Setup/Configure/Run; the main @@ -18,6 +19,8 @@ namespace llarp::apple // after construction. VPNInterface::packet_write_callback m_PacketWriter; VPNInterface::on_readable_callback m_OnReadable; + llarp_route_callbacks route_callbacks{}; + void* callback_context = nullptr; }; } // namespace llarp::apple diff --git a/llarp/apple/context_wrapper.cpp b/llarp/apple/context_wrapper.cpp index 3d1eb2a37..2ede9a498 100644 --- a/llarp/apple/context_wrapper.cpp +++ b/llarp/apple/context_wrapper.cpp @@ -19,6 +19,8 @@ namespace { llarp::apple::Context context; std::thread runner; + packet_writer_callback packet_writer; + start_reading_callback start_reading; std::weak_ptr iface; }; @@ -26,19 +28,13 @@ namespace } // namespace void* -llarp_apple_init( - ns_logger_callback ns_logger, - const char* config_dir_, - const char* default_bootstrap, - char* ip, - char* netmask, - char* dns) +llarp_apple_init(llarp_apple_config* appleconf) { - llarp::LogContext::Instance().logStream = std::make_unique(ns_logger); + llarp::LogContext::Instance().logStream = std::make_unique(appleconf->ns_logger); try { - auto config_dir = fs::u8path(config_dir_); + auto config_dir = fs::u8path(appleconf->config_dir); auto config = std::make_shared(config_dir); fs::path config_path = config_dir / "lokinet.ini"; if (!fs::exists(config_path)) @@ -59,10 +55,10 @@ llarp_apple_init( auto mask = llarp::net::TruncateV6(range.netmask_bits).ToString(); if (addr.size() > 15 || mask.size() > 15) throw std::runtime_error{"Unexpected non-IPv4 tunnel range configured"}; - std::strcpy(ip, addr.c_str()); - std::strcpy(netmask, mask.c_str()); + std::strcpy(appleconf->tunnel_ipv4_ip, addr.c_str()); + std::strcpy(appleconf->tunnel_ipv4_netmask, mask.c_str()); // XXX possibly DNS needs to be the .0 instead of the .1 because mac reasons? - std::strcpy(dns, addr.c_str()); + std::strcpy(appleconf->tunnel_dns, addr.c_str()); // The default DNS bind setting just isn't something we can use as a non-root network extension // so remap the default value to a high port unless explicitly set to something else. @@ -71,10 +67,16 @@ llarp_apple_init( // If no explicit bootstrap then set the system default one included with the app bundle if (config->bootstrap.files.empty()) - config->bootstrap.files.push_back(fs::u8path(default_bootstrap)); + config->bootstrap.files.push_back(fs::u8path(appleconf->default_bootstrap)); auto inst = std::make_unique(); inst->context.Configure(std::move(config)); + inst->context.route_callbacks = appleconf->route_callbacks; + + inst->packet_writer = appleconf->packet_writer; + inst->start_reading = appleconf->start_reading; + + return inst.release(); } catch (const std::exception& e) @@ -87,21 +89,22 @@ llarp_apple_init( int llarp_apple_start( void* lokinet, - packet_writer_callback packet_writer, - start_reading_callback start_reading, void* callback_context) { auto* inst = static_cast(lokinet); - inst->context.m_PacketWriter = [inst, packet_writer, callback_context]( - int af_family, void* data, size_t size) { - packet_writer(af_family, data, size, callback_context); - return true; + + 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_OnReadable = - [inst, start_reading, callback_context](llarp::apple::VPNInterface& iface) { + [inst, callback_context](llarp::apple::VPNInterface& iface) { inst->iface = iface.weak_from_this(); - start_reading(callback_context); + inst->start_reading(callback_context); }; std::promise result; diff --git a/llarp/apple/context_wrapper.h b/llarp/apple/context_wrapper.h index 6522d641a..279dd7cf1 100644 --- a/llarp/apple/context_wrapper.h +++ b/llarp/apple/context_wrapper.h @@ -12,70 +12,112 @@ extern "C" #include /// 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; + } 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 DNS server IPv4 address the OS should use. Null-terminated. + char tunnel_dns[16]; + + /// \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 + /// (if /lokinet.ini exists). Does not actually start lokinet (call llarp_apple_start /// for that). /// /// Returns NULL if there was a problem initializing/loading the configuration, otherwise returns /// an opaque void pointer that should be passed into the other llarp_apple_* functions. /// - /// \param logger a logger callback that we pass log messages to to relay them (i.e. via NSLog). - /// - /// \param config_dir the lokinet configuration directory where lokinet.ini can be and the various - /// other lokinet state files go. - /// - /// \param default_bootstrap the path to the default bootstrap.signed included in installation, - /// which will be used if no explicit bootstrap is set in the config file. - /// - /// \param ip - char buffer where we will write the primary tunnel IP address as a string such as - /// "172.16.0.0". Will write up to 16 characters (including the null terminator). This will be - /// the tunnel IP from the lokinet.ini, if it exists and specifies a range, otherwise we'll - /// configure lokinet to use a currently-unused range and return that. - /// - /// \param netmask the tunnel netmask as a string such as "255.255.0.0". Will write up to 16 - /// characters (including the null terminator). - /// - /// \param dns the DNS address that should be configured to query lokinet, as a string such as - /// "172.16.0.1". Will write up to 16 characters (including the null terminator). + /// \param config pointer to a llarp_apple_config where we get the various settings needed + /// and return the ip/mask/dns fields needed for the tunnel. void* - llarp_apple_init( - ns_logger_callback ns_logger, - const char* config_dir, - const char* default_bootstrap, - char* ip, - char* netmask, - char* dns); + llarp_apple_init(llarp_apple_config* config); /// Starts the lokinet instance in a new thread. /// - /// \param packet_writer 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, the size - /// of the data in bytes, and the opaque callback_context pointer. - /// - /// \param start_reading 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. This is called with a single argument of the opaque - /// callback_context pointer. + /// \param lokinet the void pointer returned by llarp_apple_init /// - /// \param callback_context Opaque pointer that is passed into the packet_writer and start_reading - /// callback, intended to allow context to be passed through to the callbacks. This code does - /// nothing with this pointer aside from passing it through to callbacks. + /// \param callback_context Opaque pointer that is passed into the various callbacks provided to + /// llarp_apple_init. This code does nothing with this pointer aside from passing it through to + /// callbacks. /// /// \returns 0 on succesful startup, -1 on failure. int - llarp_apple_start( - void* lokinet, - packet_writer_callback packet_writer, - start_reading_callback start_reading, - void* callback_context); + llarp_apple_start(void* lokinet, void* callback_context); /// Called to deliver an incoming packet from the apple layer into lokinet; returns 0 on success, /// -1 if the packet could not be parsed, -2 if there is no current active VPNInterface associated diff --git a/llarp/apple/route_manager.cpp b/llarp/apple/route_manager.cpp new file mode 100644 index 000000000..72310e953 --- /dev/null +++ b/llarp/apple/route_manager.cpp @@ -0,0 +1,61 @@ +#include "route_manager.hpp" + +namespace llarp::apple { + +void RouteManager::AddDefaultRouteViaInterface(std::string) +{ + LogWarn("AddDefaultRouteViaInterface with cbctx=", (bool) callback_context, ", adr=", (bool) route_callbacks.add_default_route); + if (callback_context and route_callbacks.add_default_route) + route_callbacks.add_default_route(callback_context); +} + +void RouteManager::DelDefaultRouteViaInterface(std::string) +{ + LogWarn("DelDefaultRouteViaInterface with cbctx=", (bool) callback_context, ", ddr=", (bool) route_callbacks.del_default_route); + if (callback_context and route_callbacks.del_default_route) + route_callbacks.del_default_route(callback_context); +} + +void +RouteManager::AddRouteViaInterface(vpn::NetworkInterface&, IPRange range) +{ + LogWarn("AddRoute with cbctx=", (bool) callback_context, ", a4r=", (bool) route_callbacks.add_ipv4_route, + "a6r", (bool) route_callbacks.add_ipv6_route); + + 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); + } + } +} + +void +RouteManager::DelRouteViaInterface(vpn::NetworkInterface&, IPRange range) +{ + LogWarn("DelRoute with cbctx=", (bool) callback_context, ", a4r=", (bool) route_callbacks.del_ipv4_route, + "a6r", (bool) route_callbacks.del_ipv6_route); + + 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); + } + } +} + +} diff --git a/llarp/apple/route_manager.hpp b/llarp/apple/route_manager.hpp new file mode 100644 index 000000000..575408888 --- /dev/null +++ b/llarp/apple/route_manager.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include "context_wrapper.h" + +namespace llarp::apple { + +class RouteManager final : public llarp::vpn::IRouteManager { +public: + RouteManager(llarp_route_callbacks rcs, void* callback_context) + : 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 {} + + void + DelRoute(IPVariant_t ip, IPVariant_t gateway) override {} + + void + AddDefaultRouteViaInterface(std::string ifname) override; + + void + DelDefaultRouteViaInterface(std::string ifname) override; + + void + AddRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override; + + void + DelRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override; + + virtual std::vector + 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 ret; + ret.push_back(huint32_t{0}); + return ret; + } + + void* callback_context = nullptr; + llarp_route_callbacks route_callbacks; +}; + +} diff --git a/llarp/apple/vpn_platform.hpp b/llarp/apple/vpn_platform.hpp index 72018b509..2f56974fd 100644 --- a/llarp/apple/vpn_platform.hpp +++ b/llarp/apple/vpn_platform.hpp @@ -2,6 +2,7 @@ #include #include "vpn_interface.hpp" +#include "route_manager.hpp" namespace llarp::apple { @@ -11,8 +12,11 @@ namespace llarp::apple explicit VPNPlatform( Context& ctx, VPNInterface::packet_write_callback packet_writer, - VPNInterface::on_readable_callback on_readable) + VPNInterface::on_readable_callback on_readable, + llarp_route_callbacks route_callbacks, + void* callback_context) : m_Context{ctx} + , m_RouteManager{std::move(route_callbacks), callback_context} , m_PacketWriter{std::move(packet_writer)} , m_OnReadable{std::move(on_readable)} {} @@ -22,8 +26,11 @@ namespace llarp::apple return std::make_shared(m_Context, m_PacketWriter, m_OnReadable); } + vpn::IRouteManager& RouteManager() override { return m_RouteManager; } + private: Context& m_Context; + apple::RouteManager m_RouteManager; VPNInterface::packet_write_callback m_PacketWriter; VPNInterface::on_readable_callback m_OnReadable; };