Cross-platform Userspace Implementation

While WireGuard has initially been developed for the Linux kernel, for maximum performance, it may run in userspace using a separate implementation. Currently wireguard-go is quite functional, and wireguard-rs is on its way. Any time in the documentation you see ip link add wg0 type wireguard, you can instead write, wireguard-go wg0. Everything else should then be identical.

In order to prevent fragmentation, all userspace implementations should conform to the same protocol and specification, thereby having the exact same behavior as the original Linux kernel one. Furthermore, it should abide by the following configuration interface.

Interface

A userspace implementation should have the following extremely limited command line interface:

# userspace-wg [-f/--foreground] INTERFACE-NAME

For example, a Go implementation would be invoked as follows for creating a wg0 interface:

# wireguard-go wg0

Running the above command would create a virtual TUN device called wg0, and then daemonize. After successfully daemonizing and bringing up the interface, it creates /var/run/wireguard/wg0.sock (or /run/wireguard/wg0.sock depending on the platform), as a UNIX domain socket operating in stream mode. On Windows the same semantics are used with a bidirectional named pipe in \\.\pipe\WireGuard\wg0.

The wg(8) tool is used for configuring the interface, so that there is complete uniformity in configuration interfaces across implementations. The wg(8) tool will look for interfaces in /var/run/wireguard/*.sock (or /run/wireguard/*.sock). Userspace implementations should die gracefully in response to SIGINT/SIGTERM, the removal of the tun interface, or the deletion of the UNIX domain socket file. The wg(8) tool connects to these sockets and sends and receives the following text-based protocol.

Configuration Protocol

A WireGuard implementation must respond to two commands: get and set, both at version 1 as of writing. wg(8) sends a get command that looks like this:

get=1
{empty line}

wg(8) sends a set command that looks like this:

set=1
key1=value1
key2=value2
key3=value3
key4=value4
key5=value5
...
{empty line}

A userspace implementation responds to a get command with:

key1=value1
key2=value2
key3=value3
key4=value4
key5=value5
...
errno=0
{empty line}

A userspace implementation responds to a set command with:

errno=0
{empty line}

If there was an error, errno is the corresponding integer from errno.h.

Note the empty line at the end of the get and set commands as well as at the end of each of their respective responses from wg(8).

The keys and values are as follows:

All interface-level keys must proceed all per-level keys.

Example Dialog

wg(8) sends:

get=1
{empty line}

Userspace WireGuard implementation responds:

private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a
listen_port=12912
public_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33
preshared_key=188515093e952f5f22e865cef3012e72f8b5f0b598ac0309d5dacce3b70fcf52
allowed_ip=192.168.4.4/32
endpoint=[abcd:23::33%2]:51820
public_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376
tx_bytes=38333
rx_bytes=2224
allowed_ip=192.168.4.6/32
persistent_keepalive_interval=111
endpoint=182.122.22.19:3233
public_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58
endpoint=5.152.198.39:51820
allowed_ip=192.168.4.10/32
allowed_ip=192.168.4.11/32
tx_bytes=1212111
rx_bytes=1929999999
protocol_version=1
errno=0
{empty line}

wg(8) sends:

set=1
private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a
fwmark=0
listen_port=12912
replace_peers=true
public_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33
preshared_key=188515093e952f5f22e865cef3012e72f8b5f0b598ac0309d5dacce3b70fcf52
replace_allowed_ips=true
allowed_ip=192.168.4.4/32
endpoint=[abcd:23::33%2]:51820
public_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376
replace_allowed_ips=true
allowed_ip=192.168.4.6/32
persistent_keepalive_interval=111
endpoint=182.122.22.19:3233
public_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58
endpoint=5.152.198.39:51820
replace_allowed_ips=true
allowed_ip=192.168.4.10/32
allowed_ip=192.168.4.11/32
public_key=e818b58db5274087fcc1be5dc728cf53d3b5726b4cef6b9bab8f8f8c2452c25c
remove=true
{empty line}

Userspace WireGuard implementation responds:

errno=0
{empty line}