Skip to content

Commit

Permalink
Implementation of split real/virtual packet tunnel in WireGuardGo for…
Browse files Browse the repository at this point in the history
… IAN
  • Loading branch information
acb-mv authored and pinkisemils committed Jul 9, 2024
1 parent 15242e1 commit cdd6038
Show file tree
Hide file tree
Showing 7 changed files with 1,122 additions and 728 deletions.
24 changes: 20 additions & 4 deletions Sources/WireGuardKit/WireGuardAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ public class WireGuardAdapter {
completionHandler(.invalidState)
return
}

guard let privateAddress = tunnelConfiguration.interface.addresses.compactMap({ $0.address as? IPv4Address }).first else
{
self.logHandler(.error, "WireGuardAdapter.start: No private IPv4 address found")
completionHandler(.invalidState)
return
}

self.addDefaultPathObserver()

Expand All @@ -205,7 +212,7 @@ public class WireGuardAdapter {
self.logEndpointResolutionResults(resolutionResults)

self.state = .started(
try self.startWireGuardBackend(wgConfig: wgConfig),
try self.startWireGuardBackend(wgConfig: wgConfig, privateAddress: privateAddress),
settingsGenerator
)

Expand Down Expand Up @@ -408,12 +415,14 @@ public class WireGuardAdapter {
/// - Parameter wgConfig: WireGuard configuration
/// - Throws: an error of type `WireGuardAdapterError`
/// - Returns: tunnel handle
private func startWireGuardBackend(wgConfig: String) throws -> Int32 {
private func startWireGuardBackend(wgConfig: String, privateAddress: IPAddress) throws -> Int32 {
guard let tunnelFileDescriptor = self.tunnelFileDescriptor else {
throw WireGuardAdapterError.cannotLocateTunnelFileDescriptor
}

let handle = wgTurnOn(wgConfig, tunnelFileDescriptor)
let privateAddr = "\(privateAddress)"

let handle = wgTurnOnIAN(wgConfig, tunnelFileDescriptor, privateAddr)
if handle < 0 {
throw WireGuardAdapterError.startWireGuardBackend(handle)
}
Expand Down Expand Up @@ -510,6 +519,13 @@ public class WireGuardAdapter {
guard isSatisfiable else { return }

self.logHandler(.verbose, "Connectivity online, resuming backend.")

guard let privateAddress = settingsGenerator.tunnelConfiguration.interface.addresses.compactMap({ $0.address as? IPv4Address }).first else
{
self.logHandler(.error, "WireGuardAdapter.start: No private IPv4 address found")
return
}


do {
try self.setNetworkSettings(settingsGenerator.generateNetworkSettings())
Expand All @@ -518,7 +534,7 @@ public class WireGuardAdapter {
self.logEndpointResolutionResults(resolutionResults)

self.state = .started(
try self.startWireGuardBackend(wgConfig: wgConfig),
try self.startWireGuardBackend(wgConfig: wgConfig, privateAddress: privateAddress),
settingsGenerator
)
} catch {
Expand Down
113 changes: 96 additions & 17 deletions Sources/WireGuardKitGo/api-apple.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import "C"
import (
"fmt"
"math"
"net/netip"
"os"
"os/signal"
"runtime"
Expand All @@ -28,6 +29,7 @@ import (
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
)

var loggerFunc unsafe.Pointer
Expand All @@ -52,8 +54,9 @@ func (l CLogger) Printf(format string, args ...interface{}) {
}

type tunnelHandle struct {
*device.Device
*device.Logger
wireGuardDevice *device.Device
virtualNet *netstack.Net
logger *device.Logger
}

var tunnelHandles = make(map[int32]tunnelHandle)
Expand Down Expand Up @@ -120,7 +123,7 @@ func wgTurnOn(settings *C.char, tunFd int32) int32 {
logger.Verbosef("Device started")

var i int32
for i = 0; i < math.MaxInt32; i++ {
for i = 1; i < math.MaxInt32; i++ {
if _, exists := tunnelHandles[i]; !exists {
break
}
Expand All @@ -129,29 +132,105 @@ func wgTurnOn(settings *C.char, tunFd int32) int32 {
unix.Close(dupTunFd)
return -1
}
tunnelHandles[i] = tunnelHandle{dev, logger}
tunnelHandles[i] = tunnelHandle{dev, nil, logger}
return i
}

//export wgTurnOnIAN
func wgTurnOnIAN(settings *C.char, tunFd int32, privateIP *C.char) int32 {
logger := &device.Logger{
Verbosef: CLogger(0).Printf,
Errorf: CLogger(1).Printf,
}

privateAddrStr := C.GoString(privateIP)
privateAddr, err := netip.ParseAddr(privateAddrStr)
if err != nil {
logger.Errorf("Invalid address: %s", privateAddrStr)
return -1
}

dupTunFd, err := unix.Dup(int(tunFd))
if err != nil {
logger.Errorf("Unable to dup tun fd: %v", err)
return -2
}

err = unix.SetNonblock(dupTunFd, true)
if err != nil {
logger.Errorf("Unable to set tun fd as non blocking: %v", err)
unix.Close(dupTunFd)
return -3
}
tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
if err != nil {
logger.Errorf("Unable to create new tun device from fd: %v", err)
unix.Close(dupTunFd)
return -4
}
/// assign the same private IPs associated with your key
vtun, virtualNet, err := netstack.CreateNetTUN([]netip.Addr{privateAddr}, []netip.Addr{}, 1280)

if err != nil {
logger.Errorf("Failed to initialize virtual tunnel device: %v", err)
tun.Close()
return -5
}

if virtualNet == nil {
logger.Errorf("Failed to initialize virtual tunnel device")
tun.Close()
return -6
}

wrapper := NewRouter(tun, vtun)
logger.Verbosef("Attaching to interface")
dev := device.NewDevice(&wrapper, conn.NewStdNetBind(), logger)

err = dev.IpcSet(C.GoString(settings))
if err != nil {
logger.Errorf("Unable to set IPC settings: %v", err)
dev.Close()
return -7
}

dev.Up()
logger.Verbosef("Device started")

var i int32
for i = 0; i < math.MaxInt32; i++ {
if _, exists := tunnelHandles[i]; !exists {
break
}
}
if i == math.MaxInt32 {
dev.Close()
return -8
}
tunnelHandles[i] = tunnelHandle{dev, virtualNet, logger}
// TODO: add a tunnel handle, or otherwise make sure we can create connections in the tunnel
return i
}

//export wgTurnOff
func wgTurnOff(tunnelHandle int32) {
dev, ok := tunnelHandles[tunnelHandle]
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return
}
delete(tunnelHandles, tunnelHandle)
dev.Close()
handle.wireGuardDevice.Close()
}

//export wgSetConfig
func wgSetConfig(tunnelHandle int32, settings *C.char) int64 {
dev, ok := tunnelHandles[tunnelHandle]
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return 0
}
err := dev.IpcSet(C.GoString(settings))
err := handle.wireGuardDevice.IpcSet(C.GoString(settings))
if err != nil {
dev.Errorf("Unable to set IPC settings: %v", err)
handle.logger.Errorf("Unable to set IPC settings: %v", err)
if ipcErr, ok := err.(*device.IPCError); ok {
return ipcErr.ErrorCode()
}
Expand All @@ -162,11 +241,11 @@ func wgSetConfig(tunnelHandle int32, settings *C.char) int64 {

//export wgGetConfig
func wgGetConfig(tunnelHandle int32) *C.char {
device, ok := tunnelHandles[tunnelHandle]
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return nil
}
settings, err := device.IpcGet()
settings, err := handle.wireGuardDevice.IpcGet()
if err != nil {
return nil
}
Expand All @@ -175,21 +254,21 @@ func wgGetConfig(tunnelHandle int32) *C.char {

//export wgBumpSockets
func wgBumpSockets(tunnelHandle int32) {
dev, ok := tunnelHandles[tunnelHandle]
handle, ok := tunnelHandles[tunnelHandle]
if !ok {
return
}
go func() {
for i := 0; i < 10; i++ {
err := dev.BindUpdate()
err := handle.wireGuardDevice.BindUpdate()
if err == nil {
dev.SendKeepalivesToPeersWithCurrentKeypair()
handle.wireGuardDevice.SendKeepalivesToPeersWithCurrentKeypair()
return
}
dev.Errorf("Unable to update bind, try %d: %v", i+1, err)
handle.logger.Errorf("Unable to update bind, try %d: %v", i+1, err)
time.Sleep(time.Second / 2)
}
dev.Errorf("Gave up trying to update bind; tunnel is likely dysfunctional")
handle.logger.Errorf("Gave up trying to update bind; tunnel is likely dysfunctional")
}()
}

Expand All @@ -199,7 +278,7 @@ func wgDisableSomeRoamingForBrokenMobileSemantics(tunnelHandle int32) {
if !ok {
return
}
dev.DisableSomeRoamingForBrokenMobileSemantics()
dev.wireGuardDevice.DisableSomeRoamingForBrokenMobileSemantics()
}

//export wgVersion
Expand Down
17 changes: 12 additions & 5 deletions Sources/WireGuardKitGo/go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
module golang.zx2c4.com/wireguard/apple

go 1.17
go 1.20

require (
golang.org/x/sys v0.5.0
golang.zx2c4.com/wireguard v0.0.0-20230209153558-1e2c3e5a3c14
golang.org/x/sys v0.12.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
)

require (
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/net v0.6.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
)
Loading

0 comments on commit cdd6038

Please sign in to comment.