From 40eeafc99decc3fda9e4b18888d3babf79ef35a1 Mon Sep 17 00:00:00 2001 From: Sergey Abramchuk Date: Mon, 20 Mar 2017 21:23:00 +0300 Subject: [PATCH] Add support of IPv6 addresses --- OpenVPN Adapter/OpenVPNAdapter+Internal.h | 13 +- OpenVPN Adapter/OpenVPNAdapter.mm | 182 ++++++++++++++++------ OpenVPN Adapter/OpenVPNClient.mm | 31 +--- OpenVPN Adapter/TUNConfiguration.h | 17 +- OpenVPN Adapter/TUNConfiguration.m | 3 +- 5 files changed, 155 insertions(+), 91 deletions(-) diff --git a/OpenVPN Adapter/OpenVPNAdapter+Internal.h b/OpenVPN Adapter/OpenVPNAdapter+Internal.h index 89dd89c..44c0779 100644 --- a/OpenVPN Adapter/OpenVPNAdapter+Internal.h +++ b/OpenVPN Adapter/OpenVPNAdapter+Internal.h @@ -17,17 +17,18 @@ using namespace openvpn; - (BOOL)configureSockets; -- (BOOL)setRemoteAddress:(NSString *)address; +- (BOOL)setRemoteAddress:(NSString *)address isIPv6:(BOOL)isIPv6; -- (BOOL)addLocalAddress:(NSString *)address subnet:(NSString *)subnet gateway:(NSString *)gateway; +- (BOOL)addLocalAddress:(NSString *)address prefixLength:(NSNumber *)prefixLength gateway:(NSString *)gateway isIPv6:(BOOL)isIPv6; -- (BOOL)addRoute:(NSString *)route subnet:(NSString *)subnet; -- (BOOL)excludeRoute:(NSString *)route subnet:(NSString *)subnet; +- (BOOL)defaultGatewayRerouteIPv4:(BOOL)rerouteIPv4 rerouteIPv6:(BOOL)rerouteIPv6; +- (BOOL)addRoute:(NSString *)route prefixLength:(NSNumber *)prefixLength isIPv6:(BOOL)isIPv6; +- (BOOL)excludeRoute:(NSString *)route prefixLength:(NSNumber *)prefixLength isIPv6:(BOOL)isIPv6; -- (BOOL)addDNSAddress:(NSString *)address; +- (BOOL)addDNSAddress:(NSString *)address isIPv6:(BOOL)isIPv6; - (BOOL)addSearchDomain:(NSString *)domain; -- (BOOL)setMTU:(NSInteger)mtu; +- (BOOL)setMTU:(NSNumber *)mtu; - (NSInteger)establishTunnel; diff --git a/OpenVPN Adapter/OpenVPNAdapter.mm b/OpenVPN Adapter/OpenVPNAdapter.mm index ebd2817..bcfc2a4 100644 --- a/OpenVPN Adapter/OpenVPNAdapter.mm +++ b/OpenVPN Adapter/OpenVPNAdapter.mm @@ -38,15 +38,23 @@ NSString * const OpenVPNAdapterErrorEventKey = @"me.ss-abramchuk.openvpn-adapter @property OpenVPNClient *vpnClient; -@property (strong, nonatomic) TUNConfiguration *tunConfiguration; - @property CFSocketRef vpnSocket; @property CFSocketRef tunSocket; +@property (strong, nonatomic) NSString *remoteAddress; + +@property (strong, nonatomic) TUNConfiguration *tunConfigurationIPv6; +@property (strong, nonatomic) TUNConfiguration *tunConfigurationIPv4; + +@property (strong, nonatomic) NSMutableArray *searchDomains; + +@property (strong, nonatomic) NSNumber *mtu; + @property (weak, nonatomic) id packetFlow; - (void)readTUNPackets; - (void)readVPNData:(NSData *)data; +- (NSString *)getSubnetFromPrefixLength:(NSNumber *)prefixLength; @end @@ -94,108 +102,159 @@ static void socketCallback(CFSocketRef socket, CFSocketCallBackType type, CFData #pragma mark TUN Configuration -- (BOOL)setRemoteAddress:(NSString *)address { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); - +- (BOOL)setRemoteAddress:(NSString *)address isIPv6:(BOOL)isIPv6 { if (address == nil) { return NO; } - self.tunConfiguration.remoteAddress = address; + self.remoteAddress = address; return YES; } -- (BOOL)addLocalAddress:(NSString *)address subnet:(NSString *)subnet gateway:(NSString *)gateway { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); - - if (address == nil || subnet == nil) { +- (BOOL)addLocalAddress:(NSString *)address prefixLength:(NSNumber *)prefixLength gateway:(NSString *)gateway isIPv6:(BOOL)isIPv6 { + if (address == nil || prefixLength == nil) { return NO; } - [self.tunConfiguration.localAddresses addObject:address]; - [self.tunConfiguration.subnets addObject:subnet]; + if (isIPv6) { + [self.tunConfigurationIPv6.localAddresses addObject:address]; + [self.tunConfigurationIPv6.prefixLengths addObject:prefixLength]; + } else { + [self.tunConfigurationIPv4.localAddresses addObject:address]; + [self.tunConfigurationIPv4.prefixLengths addObject:prefixLength]; + } return YES; } -- (BOOL)addRoute:(NSString *)route subnet:(NSString *)subnet { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); +- (BOOL)defaultGatewayRerouteIPv4:(BOOL)rerouteIPv4 rerouteIPv6:(BOOL)rerouteIPv6 { + if (rerouteIPv6) { + NEIPv6Route *includedRoute = [NEIPv6Route defaultRoute]; + [self.tunConfigurationIPv6.includedRoutes addObject:includedRoute]; + } - if (route == nil || subnet == nil) { + if (rerouteIPv4) { + NEIPv4Route *includedRoute = [NEIPv4Route defaultRoute]; + [self.tunConfigurationIPv4.includedRoutes addObject:includedRoute]; + } + + return YES; +} + +- (BOOL)addRoute:(NSString *)route prefixLength:(NSNumber *)prefixLength isIPv6:(BOOL)isIPv6 { + if (route == nil || prefixLength == nil) { return NO; } - NEIPv4Route *includedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnet]; - [self.tunConfiguration.includedRoutes addObject:includedRoute]; + if (isIPv6) { + NEIPv6Route *includedRoute = [[NEIPv6Route alloc] initWithDestinationAddress:route networkPrefixLength:prefixLength]; + [self.tunConfigurationIPv6.includedRoutes addObject:includedRoute]; + } else { + NSString *subnet = [self getSubnetFromPrefixLength:prefixLength]; + NEIPv4Route *includedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnet]; + [self.tunConfigurationIPv4.includedRoutes addObject:includedRoute]; + } return YES; } -- (BOOL)excludeRoute:(NSString *)route subnet:(NSString *)subnet { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); - - if (route == nil || subnet == nil) { +- (BOOL)excludeRoute:(NSString *)route prefixLength:(NSNumber *)prefixLength isIPv6:(BOOL)isIPv6 { + if (route == nil || prefixLength == nil) { return NO; } - NEIPv4Route *excludedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnet]; - [self.tunConfiguration.excludedRoutes addObject:excludedRoute]; + if (isIPv6) { + NEIPv6Route *excludedRoute = [[NEIPv6Route alloc] initWithDestinationAddress:route networkPrefixLength:prefixLength]; + [self.tunConfigurationIPv6.excludedRoutes addObject:excludedRoute]; + } else { + NSString *subnet = [self getSubnetFromPrefixLength:prefixLength]; + NEIPv4Route *excludedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnet]; + [self.tunConfigurationIPv4.excludedRoutes addObject:excludedRoute]; + } return YES; } -- (BOOL)addDNSAddress:(NSString *)address { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); - +- (BOOL)addDNSAddress:(NSString *)address isIPv6:(BOOL)isIPv6 { if (address == nil) { return NO; } - [self.tunConfiguration.dnsAddresses addObject:address]; + if (isIPv6) { + [self.tunConfigurationIPv6.dnsAddresses addObject:address]; + } else { + [self.tunConfigurationIPv4.dnsAddresses addObject:address]; + } return YES; } - (BOOL)addSearchDomain:(NSString *)domain { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); - if (domain == nil) { return NO; } - [self.tunConfiguration.searchDomains addObject:domain]; + [self.searchDomains addObject:domain]; return YES; } -- (BOOL)setMTU:(NSInteger)mtu { - NSAssert(self.tunConfiguration != nil, @"TUN configuration should be initialized"); - - self.tunConfiguration.mtu = @(mtu); - +- (BOOL)setMTU:(NSNumber *)mtu { + self.mtu = mtu; return YES; } - (NSInteger)establishTunnel { NSAssert(self.delegate != nil, @"delegate property should not be nil"); - NEIPv4Settings *ipSettings = [[NEIPv4Settings alloc] initWithAddresses:@[self.tunConfiguration.localAddresses] subnetMasks:@[self.tunConfiguration.subnets]]; - ipSettings.includedRoutes = self.tunConfiguration.includedRoutes; - ipSettings.excludedRoutes = self.tunConfiguration.excludedRoutes; + NEPacketTunnelNetworkSettings *networkSettings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.remoteAddress]; - NEPacketTunnelNetworkSettings *networkSettings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.tunConfiguration.remoteAddress]; - networkSettings.IPv4Settings = ipSettings; - - if (self.tunConfiguration.dnsAddresses.count > 0) { - networkSettings.DNSSettings = [[NEDNSSettings alloc] initWithServers:self.tunConfiguration.dnsAddresses]; + // Configure IPv6 addresses and routes + if (self.tunConfigurationIPv6.initialized) { + NEIPv6Settings *settingsIPv6 = [[NEIPv6Settings alloc] initWithAddresses:self.tunConfigurationIPv6.localAddresses networkPrefixLengths:self.tunConfigurationIPv6.prefixLengths]; + settingsIPv6.includedRoutes = self.tunConfigurationIPv6.includedRoutes; + settingsIPv6.excludedRoutes = self.tunConfigurationIPv6.excludedRoutes; - if (self.tunConfiguration.searchDomains.count > 0) { - networkSettings.DNSSettings.searchDomains = self.tunConfiguration.searchDomains; - } + networkSettings.IPv6Settings = settingsIPv6; } - networkSettings.MTU = self.tunConfiguration.mtu; + // Configure IPv4 addresses and routes + if (self.tunConfigurationIPv4.initialized) { + NSMutableArray *subnets = [NSMutableArray new]; + [self.tunConfigurationIPv4.prefixLengths enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSString *subnet = [self getSubnetFromPrefixLength:obj]; + [subnets addObject:subnet]; + }]; + + NEIPv4Settings *ipSettings = [[NEIPv4Settings alloc] initWithAddresses:self.tunConfigurationIPv4.localAddresses subnetMasks:subnets]; + ipSettings.includedRoutes = self.tunConfigurationIPv4.includedRoutes; + ipSettings.excludedRoutes = self.tunConfigurationIPv4.excludedRoutes; + + networkSettings.IPv4Settings = ipSettings; + } + + // Configure DNS addresses and search domains + NSMutableArray *dnsAddresses = [NSMutableArray new]; + + if (self.tunConfigurationIPv6.dnsAddresses.count > 0) { + [dnsAddresses addObjectsFromArray:self.tunConfigurationIPv6.dnsAddresses]; + } + + if (self.tunConfigurationIPv4.dnsAddresses.count > 0) { + [dnsAddresses addObjectsFromArray:self.tunConfigurationIPv4.dnsAddresses]; + } + + if (dnsAddresses.count > 0) { + networkSettings.DNSSettings = [[NEDNSSettings alloc] initWithServers:dnsAddresses]; + } + + if (networkSettings.DNSSettings && self.searchDomains.count > 0) { + networkSettings.DNSSettings.searchDomains = self.searchDomains; + } + + // Set MTU + networkSettings.MTU = self.mtu; dispatch_semaphore_t sema = dispatch_semaphore_create(0); @@ -368,7 +427,11 @@ static void socketCallback(CFSocketRef socket, CFSocketCallBackType type, CFData // TODO: Describe why we use async invocation here dispatch_queue_t connectQueue = dispatch_queue_create("me.ss-abramchuk.openvpn-ios-client.connection", NULL); dispatch_async(connectQueue, ^{ - self.tunConfiguration = [TUNConfiguration new]; + self.tunConfigurationIPv6 = [TUNConfiguration new]; + self.tunConfigurationIPv4 = [TUNConfiguration new]; + + self.searchDomains = [NSMutableArray new]; + OpenVPNClient::init_process(); try { @@ -391,7 +454,15 @@ static void socketCallback(CFSocketRef socket, CFSocketCallBackType type, CFData } OpenVPNClient::uninit_process(); - self.tunConfiguration = nil; + + self.remoteAddress = nil; + + self.tunConfigurationIPv6 = nil; + self.tunConfigurationIPv4 = nil; + + self.searchDomains = nil; + + self.mtu = nil; self.username = nil; self.password = nil; @@ -475,6 +546,19 @@ static void socketCallback(CFSocketRef socket, CFSocketCallBackType type, CFData } } +#pragma mark Utils + +- (NSString *)getSubnetFromPrefixLength:(NSNumber *)prefixLength { + uint32_t bitmask = UINT_MAX << (sizeof(uint32_t) * 8 - prefixLength.integerValue); + + uint8_t first = (bitmask >> 24) & 0xFF; + uint8_t second = (bitmask >> 16) & 0xFF; + uint8_t third = (bitmask >> 8) & 0xFF; + uint8_t fourth = bitmask & 0xFF; + + return [NSString stringWithFormat:@"%uc.%uc.%uc.%uc", first, second, third, fourth]; +} + #pragma mark Deallocation - (void)dealloc { diff --git a/OpenVPN Adapter/OpenVPNClient.mm b/OpenVPN Adapter/OpenVPNClient.mm index d2df7d3..16b6dc6 100644 --- a/OpenVPN Adapter/OpenVPNClient.mm +++ b/OpenVPN Adapter/OpenVPNClient.mm @@ -24,49 +24,34 @@ bool OpenVPNClient::tun_builder_new() { } bool OpenVPNClient::tun_builder_set_remote_address(const std::string &address, bool ipv6) { - // TODO: Adapter should be able to set IPv6 remote address - NSString *remoteAddress = [NSString stringWithUTF8String:address.c_str()]; - return [(__bridge OpenVPNAdapter *)adapter setRemoteAddress:remoteAddress]; + return [(__bridge OpenVPNAdapter *)adapter setRemoteAddress:remoteAddress isIPv6:ipv6]; } bool OpenVPNClient::tun_builder_add_address(const std::string &address, int prefix_length, const std::string &gateway, bool ipv6, bool net30) { - // TODO: Adapter should be able to add IPv6 addresses - NSString *localAddress = [NSString stringWithUTF8String:address.c_str()]; - NSString *subnet = [NSString stringWithUTF8String:get_subnet(prefix_length).c_str()]; NSString *gatewayAddress = [NSString stringWithUTF8String:gateway.c_str()]; - return [(__bridge OpenVPNAdapter *)adapter addLocalAddress:localAddress subnet:subnet gateway:gatewayAddress]; + return [(__bridge OpenVPNAdapter *)adapter addLocalAddress:localAddress prefixLength:@(prefix_length) gateway:gatewayAddress isIPv6:ipv6]; } bool OpenVPNClient::tun_builder_reroute_gw(bool ipv4, bool ipv6, unsigned int flags) { - return true; + return [(__bridge OpenVPNAdapter *)adapter defaultGatewayRerouteIPv4:ipv4 rerouteIPv6:ipv6]; } bool OpenVPNClient::tun_builder_add_route(const std::string& address, int prefix_length, int metric, bool ipv6) { - // TODO: Adapter should be able to add IPv6 routes - NSString *route = [NSString stringWithUTF8String:address.c_str()]; - NSString *subnet = [NSString stringWithUTF8String:get_subnet(prefix_length).c_str()]; - - return [(__bridge OpenVPNAdapter *)adapter addRoute:route subnet:subnet]; + return [(__bridge OpenVPNAdapter *)adapter addRoute:route prefixLength:@(prefix_length) isIPv6:ipv6]; } bool OpenVPNClient::tun_builder_exclude_route(const std::string& address, int prefix_length, int metric, bool ipv6) { - // TODO: Adapter should be able to exclude IPv6 routes - NSString *route = [NSString stringWithUTF8String:address.c_str()]; - NSString *subnet = [NSString stringWithUTF8String:get_subnet(prefix_length).c_str()]; - - return [(__bridge OpenVPNAdapter *)adapter excludeRoute:route subnet:subnet]; + return [(__bridge OpenVPNAdapter *)adapter excludeRoute:route prefixLength:@(prefix_length) isIPv6:ipv6]; } -bool OpenVPNClient::tun_builder_add_dns_server(const std::string& address, bool ipv6) { - // TODO: Adapter should be able to add IPv6 DNS - +bool OpenVPNClient::tun_builder_add_dns_server(const std::string& address, bool ipv6) { NSString *dnsAddress = [NSString stringWithUTF8String:address.c_str()]; - return [(__bridge OpenVPNAdapter *)adapter addDNSAddress:dnsAddress]; + return [(__bridge OpenVPNAdapter *)adapter addDNSAddress:dnsAddress isIPv6:ipv6]; } bool OpenVPNClient::tun_builder_add_search_domain(const std::string& domain) { @@ -75,7 +60,7 @@ bool OpenVPNClient::tun_builder_add_search_domain(const std::string& domain) { } bool OpenVPNClient::tun_builder_set_mtu(int mtu) { - return [(__bridge OpenVPNAdapter *)adapter setMTU:mtu]; + return [(__bridge OpenVPNAdapter *)adapter setMTU:@(mtu)]; } bool OpenVPNClient::tun_builder_set_session_name(const std::string& name) { diff --git a/OpenVPN Adapter/TUNConfiguration.h b/OpenVPN Adapter/TUNConfiguration.h index e4a2fb9..90743db 100644 --- a/OpenVPN Adapter/TUNConfiguration.h +++ b/OpenVPN Adapter/TUNConfiguration.h @@ -8,21 +8,16 @@ #import -@class NEIPv4Route; - @interface TUNConfiguration : NSObject -@property (strong, nonatomic) NSString *remoteAddress; +@property (nonatomic) BOOL initialized; -@property (readonly, strong, nonatomic) NSMutableArray *localAddresses; -@property (readonly, strong, nonatomic) NSMutableArray *subnets; +@property (readonly, strong, nonatomic) NSMutableArray *localAddresses; +@property (readonly, strong, nonatomic) NSMutableArray *prefixLengths; -@property (readonly, strong, nonatomic) NSMutableArray *includedRoutes; -@property (readonly, strong, nonatomic) NSMutableArray *excludedRoutes; +@property (readonly, strong, nonatomic) NSMutableArray *includedRoutes; +@property (readonly, strong, nonatomic) NSMutableArray *excludedRoutes; -@property (readonly, strong, nonatomic) NSMutableArray *dnsAddresses; -@property (readonly, strong, nonatomic) NSMutableArray *searchDomains; - -@property (strong, nonatomic) NSNumber *mtu; +@property (readonly, strong, nonatomic) NSMutableArray *dnsAddresses; @end diff --git a/OpenVPN Adapter/TUNConfiguration.m b/OpenVPN Adapter/TUNConfiguration.m index ca1c296..b8f4940 100644 --- a/OpenVPN Adapter/TUNConfiguration.m +++ b/OpenVPN Adapter/TUNConfiguration.m @@ -15,13 +15,12 @@ self = [super init]; if (self) { _localAddresses = [NSMutableArray new]; - _subnets = [NSMutableArray new]; + _prefixLengths = [NSMutableArray new]; _includedRoutes = [NSMutableArray new]; _excludedRoutes = [NSMutableArray new]; _dnsAddresses = [NSMutableArray new]; - _searchDomains = [NSMutableArray new]; } return self; }