Skip to content

WireGuard

WireGuard is a L3 connection-less tunnel protocol, based on UDP.

  • After a 1-RTT handshale, packets will be encrypted and transmitted in WireGuard Transport Data messages. No additional messages needed.
  • The transport data message takes 16Bytes, the UDP header takes 8, and the IP header takes 20 (or 40 for IPv6), leaving MTU = 1440 or 1420 (IPv6).
  • No server / client distinction. Peers are identified via public keys.
  • Peers may roam to whatever IP they want as long as they handshake with the same public key.
  • Only one side of the tunnel needs a public IP address.
  • Multiple peers per interface possible, with crypto-based internal routing and allowed IPs filter.

For the Linux implementation:

  • WireGuard interfaces are just represented as Linux virtual interfaces, and they can be managed using iproute2 and wg(1).
  • Has very well support for network namespaces.
  • Do not work well with vrf.
  • Could be hard to debug.

Basic Operation

Setup the interface and address with ordinary Linux network management tools, e.g. iproute2:

$ ip l add dev wg0 type wireguard
$ ip l set dev wg0 up
$ ip a add dev wg0 ...

Set WireGuard-specific options using wg(1):

$ wg
$ wg set nic listen-port uint16_t
$ wg set nic private-key /path/to/private-key
$ wg set peer public-key preshared-key /path/to/preshared-key
$ wg set peer public-key endpoint ip:port
$ wg set peer public-key persistent-keepalive intervals
$ wg set peer public-key allowed-ips ip/cidr
$ wg set peer public-key remove

Crypto Routing and Allowed IPs

Give a WireGuard NIC a block of IP address, and specify allowed ranges of each peer. Packets will be routed to peers based on allowed IPs. This does not go through system routing table.

TODO

Network Namespace

WireGuard will continue to listen on the originating network namespace after it is moved to new namespaces. That is, for example, you want WireGuard to bind to an Ethernet interface, but you want to route packets in a netns from / to the WireGuard interface, but that Ethernet interface is not in the netns. You can just create the WireGuard interface in the netns that has the Ethernet NIC, and use ip l set dev NIC netns NETNS to move that into the specific netns.

See wireguard.com/netns.

fwmark

TODO

wg-quick(1)

A bash script that takes the input configuration file and automatically invokes ip-link(1), ip-addr(1), wg(1), ip-route(1), and resolvconf(1). It is a oneshot script: it will exit after applying the configuration.

It doesn't support network namespaces. You can do ip-link(1) yourself and patch wg-quick(1) to ignore creating the WireGuard interface.

Sample configuration:

# /path/to/wg0.conf
[Interface]
Address =
PrivateKey = private-key
ListenPort = port
# Table = off
# DNS =

[Peer]
PublicKey = public-key
AllowedIPs = cidr, cidr, cidr
PersistentKeepalive =
Endpoint = ip:port

Note that when using its systemd service, the file has to exist when stopping the service, otherwise the stop command will fail and you have to clear the failed unit yourself.

Debugging

Could be hard though. Good luck with tcpdump(1).

For kernel module debug logs:

$ modprobe wireguard 
$ echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

Last update: November 7, 2023
Created: November 5, 2023