Overview of the Embeddable WireGuard Tunnel Library
masterembeddable-dll-service and tunnel modules from the upstream wireguard-windows repository, allowing developers to leverage existing WireGuard functionality within their own applications.repository·master·Indexed 41 days ago
https://github.com/amnezia-vpn/amneziawg-windowsAn embeddable WireGuard tunnel library for Windows that allows developers to integrate tunneling capabilities into applications via a DLL. It includes functionality for managing network interface changes via interfaceWatcher, generating Curve25519 keypairs, and running the tunnel as a Windows service with support for Wintun interfaces and UAPI listeners.
embeddable-dll-service and tunnel modules from the upstream wireguard-windows repository, allowing developers to leverage existing WireGuard functionality within their own applications.To build the embeddable DLL service, execute the build.cmd script. This will generate the tunnel.dll file in the x64 directory.
.\build.cmdThe interfaceWatcher struct is used to monitor Windows network interface changes and automatically reconfigure the tunnel's IP addresses when relevant changes occur.
watchInterface() to start monitoring. This registers a callback via winipcfg.RegisterInterfaceChangeCallback that listens for winipcfg.MibAddInstance notifications.Configure(binder, conf, tun) to provide the necessary dependencies. This method also processes any interface change events that occurred while the watcher was running but before the tunnel was ready.Destroy() to unregister all callbacks, disable the firewall, and flush routes, IP addresses, and DNS settings associated with the tunnel's LUID to ensure a clean teardown of the Windows networking stack.Errors encountered during interface setup are sent to the errors channel of the interfaceWatcher instance as interfaceWatcherError objects, which wrap a services.Error and the underlying error.
iw, err := watchInterface()
if err != nil {
log.Fatal(err)
}
// ... later, once tunnel and config are ready ...
iw.Configure(binder, config, tun)
// ... on shutdown ...
iw.Destroy()To start the AmneziaWG tunnel as a Windows service, use the Run function. This function wraps the standard Windows service execution logic, handling interface creation (Wintun), privilege dropping, firewall configuration, and UAPI listening.
confString: A string containing the WireGuard configuration (compatible with wg-quick format).tunnelName: The name of the service to be registered in the Windows Service Control Manager (SCM).confString using conf.FromWgQuickWithUnknownEncoding and deduplicates network entries.PreUp, PostUp, PreDown, and PostDown script commands defined in the configuration.services.DetermineErrorCode for better visibility in Windows Service logs.import "github.com/amnezia-vpn/amneziawg-windows/v3"
func main() {
confString := `[Interface]
PrivateKey = <key>
Address = 10.0.0.1/24`
tunnelName := "amneziawg-tunnel"
err := amneziawg.Run(confString, tunnelName)
if err != nil {
log.Fatalf("Service failed: %v", err)
}
}The Destroy() method performs a comprehensive cleanup of the interface watcher and the associated network state. It:
firewall.DisableFirewall().windows.AF_INET and windows.AF_INET6 to prevent Windows networking stack issues.iw.Destroy()The WireGuardGenerateKeypair function generates a new Curve25519 keypair. It populates two 32-byte buffers provided by the caller: one for the public key and one for the private key. The function handles the necessary bit manipulation to ensure the private key is valid for Curve25519 (clamping) and then computes the public key using curve25519.ScalarBaseMult.
// The caller must provide pointers to 32-byte arrays
// WireGuardGenerateKeypair(publicKeyPtr, privateKeyPtr)The CopyConfigOwnerToIPCSecurityDescriptor function synchronizes the permissions of the Inter-Process Communication (IPC) mechanism with the owner of a specific configuration file.
This ensures that the user who owns the configuration file also has full access (GENERIC_ALL) to the IPC interface. The function performs the following logic:
conf.PathIsEncrypted. If so, it returns nil.filename using Windows security APIs.WinLocalSystemSid, it skips the update.GENERIC_ALL access to the file owner.ipc.UAPISecurityDescriptor with a new Discretionary Access Control List (DACL) that includes this additional entry.This is used to prevent permission mismatches where a user might own a VPN configuration but be unable to communicate with the background service via IPC.
err := CopyConfigOwnerToIPCSecurityDescriptor("path/to/config.conf")
if err != nil {
// handle error
}The WireGuardTunnelService function is an exported entry point used to start the WireGuard tunnel service. It accepts two UTF-16 encoded strings: a configuration string and a name string. The function returns true if the service started successfully, and false if an error occurred. Internally, it sets UseFixedGUIDInsteadOfDeterministic to true before executing the tunnel logic.
// Note: The function expects pointers to UTF-16 strings (uint16)
// success := WireGuardTunnelService(confString16, nameString16)The Configure(binder conn.BindSocketToInterface, conf *conf.Config, tun *tun.NativeTun) method attaches the required tunnel components to the watcher. It allows the watcher to identify which interface changes are relevant (by matching the tunnel's LUID) and triggers the initial IP configuration for both IPv4 and IPv6.
iw.Configure(binder, conf, tun)The watchInterface() function initializes a new interfaceWatcher and starts monitoring for network interface changes. It returns the *interfaceWatcher instance or an error if the callback registration fails.
iw, err := watchInterface()When the Run function fails, it returns error codes mapped to specific failure points. These are used by the Windows Service Control Manager to diagnose issues. Common error mappings include:
| Error Constant | Meaning |
|---|---|
services.ErrorRingloggerOpen | Failed to initialize the global ring logger. |
services.ErrorLoadConfiguration | Failed to parse the provided configuration string. |
services.ErrorSetNetConfig | Failed to watch or configure network interfaces. |
services.ErrorDNSLookup | Failed to resolve DNS names during configuration. |
services.ErrorCreateWintun | Failed to create the Wintun TUN device. |
services.ErrorRunScript | A PreUp or PostUp script command failed. |
services.ErrorFirewall | Failed to configure the Windows firewall rules. |
services.ErrorDropPrivileges | Failed to drop privileges after setup. |
services.ErrorUAPIListen | Failed to start the UAPI listener. |
services.ErrorDeviceSetConfig | Failed to apply configuration to the device via UAPI. |
services.ErrorDeviceSetConfig | Failed to apply configuration to the device via UAPI. |
services.ErrorFirewall | Failed to configure the Windows firewall rules. |
services.ErrorDropPrivileges | Failed to drop privileges after setup. |
services.ErrorUAPIListen | Failed to start the UAPI listener. |
services.ErrorDeviceSetConfig | Failed to apply configuration to the device via UAPI. |