]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
Kit: netcfg: add explicit IP mask routes
authorJason A. Donenfeld <Jason@zx2c4.com>
Fri, 1 Jan 2021 17:26:49 +0000 (18:26 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Fri, 1 Jan 2021 17:28:14 +0000 (18:28 +0100)
macOS will use the wrong source address unless we add explicit routes
that mention the self-pointing gateway. Actually, it won't add any
implicit routes on its own, so in order to route the masks of the
addresses, we have to add our own routes explicitly.

However, this still doesn't fix the problem while inside of the network
extension, even though it works outside it.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Sources/WireGuardKit/IPAddressRange.swift
Sources/WireGuardKit/PacketTunnelSettingsGenerator.swift

index 67c3955bb11829b4975ff05dffff97b6bb433c23..ecb888eab1f4f73c8b140d8d289131f7aa58a118 100644 (file)
@@ -64,4 +64,52 @@ extension IPAddressRange {
 
         return (address, networkPrefixLength)
     }
+
+    public func subnetMask() -> IPAddress {
+        if address is IPv4Address {
+            let mask = networkPrefixLength > 0 ? ~UInt32(0) << (32 - networkPrefixLength) : UInt32(0)
+            let bytes = Data([
+                UInt8(truncatingIfNeeded: mask >> 24),
+                UInt8(truncatingIfNeeded: mask >> 16),
+                UInt8(truncatingIfNeeded: mask >> 8),
+                UInt8(truncatingIfNeeded: mask >> 0)
+            ])
+            return IPv4Address(bytes)!
+        }
+        if address is IPv6Address {
+            var bytes = Data(repeating: 0, count: 16)
+            for i in 0..<Int(networkPrefixLength/8) {
+                bytes[i] = 0xff
+            }
+            let nibble = networkPrefixLength % 32
+            if nibble != 0 {
+                let mask = ~UInt32(0) << (32 - nibble)
+                let i = Int(networkPrefixLength / 32 * 4)
+                bytes[i + 0] = UInt8(truncatingIfNeeded: mask >> 24)
+                bytes[i + 1] = UInt8(truncatingIfNeeded: mask >> 16)
+                bytes[i + 2] = UInt8(truncatingIfNeeded: mask >> 8)
+                bytes[i + 3] = UInt8(truncatingIfNeeded: mask >> 0)
+            }
+            return IPv6Address(bytes)!
+        }
+        fatalError()
+    }
+
+    public func maskedAddress() -> IPAddress {
+        let subnet = subnetMask().rawValue
+        var masked = Data(address.rawValue)
+        if subnet.count != masked.count {
+            fatalError()
+        }
+        for i in 0..<subnet.count {
+            masked[i] &= subnet[i]
+        }
+        if subnet.count == 4 {
+            return IPv4Address(masked)!
+        }
+        if subnet.count == 16 {
+            return IPv6Address(masked)!
+        }
+        fatalError()
+    }
 }
index 2d8cda1a0483c1c7b693d5711d5cdb52dc994905..7baa7cc8192f14138c044f37021983e6c6403be5 100644 (file)
@@ -113,38 +113,26 @@ class PacketTunnelSettingsGenerator {
             networkSettings.mtu = NSNumber(value: mtu)
         }
 
-        let (ipv4Routes, ipv6Routes) = routes()
+        let (ipv4Addresses, ipv6Addresses) = addresses()
         let (ipv4IncludedRoutes, ipv6IncludedRoutes) = includedRoutes()
 
-        let ipv4Settings = NEIPv4Settings(addresses: ipv4Routes.map { $0.destinationAddress }, subnetMasks: ipv4Routes.map { $0.destinationSubnetMask })
+        let ipv4Settings = NEIPv4Settings(addresses: ipv4Addresses.map { $0.destinationAddress }, subnetMasks: ipv4Addresses.map { $0.destinationSubnetMask })
         ipv4Settings.includedRoutes = ipv4IncludedRoutes
         networkSettings.ipv4Settings = ipv4Settings
 
-        let ipv6Settings = NEIPv6Settings(addresses: ipv6Routes.map { $0.destinationAddress }, networkPrefixLengths: ipv6Routes.map { $0.destinationNetworkPrefixLength })
+        let ipv6Settings = NEIPv6Settings(addresses: ipv6Addresses.map { $0.destinationAddress }, networkPrefixLengths: ipv6Addresses.map { $0.destinationNetworkPrefixLength })
         ipv6Settings.includedRoutes = ipv6IncludedRoutes
         networkSettings.ipv6Settings = ipv6Settings
 
         return networkSettings
     }
 
-    private func ipv4SubnetMaskString(of addressRange: IPAddressRange) -> String {
-        let length: UInt8 = addressRange.networkPrefixLength
-        assert(length <= 32)
-        var octets: [UInt8] = [0, 0, 0, 0]
-        let subnetMask: UInt32 = length > 0 ? ~UInt32(0) << (32 - length) : UInt32(0)
-        octets[0] = UInt8(truncatingIfNeeded: subnetMask >> 24)
-        octets[1] = UInt8(truncatingIfNeeded: subnetMask >> 16)
-        octets[2] = UInt8(truncatingIfNeeded: subnetMask >> 8)
-        octets[3] = UInt8(truncatingIfNeeded: subnetMask)
-        return octets.map { String($0) }.joined(separator: ".")
-    }
-
-    private func routes() -> ([NEIPv4Route], [NEIPv6Route]) {
+    private func addresses() -> ([NEIPv4Route], [NEIPv6Route]) {
         var ipv4Routes = [NEIPv4Route]()
         var ipv6Routes = [NEIPv6Route]()
         for addressRange in tunnelConfiguration.interface.addresses {
             if addressRange.address is IPv4Address {
-                ipv4Routes.append(NEIPv4Route(destinationAddress: "\(addressRange.address)", subnetMask: ipv4SubnetMaskString(of: addressRange)))
+                ipv4Routes.append(NEIPv4Route(destinationAddress: "\(addressRange.address)", subnetMask: "\(addressRange.subnetMask())"))
             } else if addressRange.address is IPv6Address {
                 /* Big fat ugly hack for broken iOS networking stack: the smallest prefix that will have
                  * any effect on iOS is a /120, so we clamp everything above to /120. This is potentially
@@ -160,10 +148,23 @@ class PacketTunnelSettingsGenerator {
     private func includedRoutes() -> ([NEIPv4Route], [NEIPv6Route]) {
         var ipv4IncludedRoutes = [NEIPv4Route]()
         var ipv6IncludedRoutes = [NEIPv6Route]()
+
+        for addressRange in tunnelConfiguration.interface.addresses {
+            if addressRange.address is IPv4Address {
+                let route = NEIPv4Route(destinationAddress: "\(addressRange.maskedAddress())", subnetMask: "\(addressRange.subnetMask())")
+                route.gatewayAddress = "\(addressRange.address)"
+                ipv4IncludedRoutes.append(route)
+            } else if addressRange.address is IPv6Address {
+                let route = NEIPv6Route(destinationAddress: "\(addressRange.maskedAddress())", networkPrefixLength: NSNumber(value: addressRange.networkPrefixLength))
+                route.gatewayAddress = "\(addressRange.address)"
+                ipv6IncludedRoutes.append(route)
+            }
+        }
+
         for peer in tunnelConfiguration.peers {
             for addressRange in peer.allowedIPs {
                 if addressRange.address is IPv4Address {
-                    ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(addressRange.address)", subnetMask: ipv4SubnetMaskString(of: addressRange)))
+                    ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(addressRange.address)", subnetMask: "\(addressRange.subnetMask())"))
                 } else if addressRange.address is IPv6Address {
                     ipv6IncludedRoutes.append(NEIPv6Route(destinationAddress: "\(addressRange.address)", networkPrefixLength: NSNumber(value: addressRange.networkPrefixLength)))
                 }