wireguard – Fixing ‘Destination Address Required’ Error in Wireguard

firewallvpnwireguard

I have a simple wireguard network comprised of a single "server" (the only device with an externally routable ip address) and two clients. Communication between the server and the clients seems to work great: from the server, I can access the clients using their wireguard addresses and I can access addresses "behind" the clients. Similarly, from the clients I can access the server's wireguard address.

What doesn't work is client-to-client communication. If from one client I attempt to ping another client using its wireguard ip address, the ping fails with:

From 192.168.64.10 icmp_seq=1 Destination Host Unreachable
ping: sendmsg: Destination address required

Furthermore, that ping attempt doesn't result in any UDP traffic between the client and the server.

I've included my wireguard configuration below.

VPN Nodes

On all nodes:

  • net.ipv4.ip_forward is 1
  • There are no restrictions on the FORWARD table
  • I am not using wg-quick to bring up the vpn. I'm using a shell script that is included at the bottom of this post.

Server

# ip addr show wg0
39: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 192.168.64.1/24 scope global wg0
       valid_lft forever preferred_lft forever
# ip route show | grep wg0
10.0.0.0/8 dev wg0 scope link
192.168.1.0/24 dev wg0 scope link
192.168.11.0/24 dev wg0 scope link
192.168.13.0/24 dev wg0 scope link
192.168.64.0/24 dev wg0 proto kernel scope link src 192.168.64.1
[Interface]
PrivateKey = <secret key>
ListenPort = 50001

[Peer]
PublicKey = 1cML7...
AllowedIps = 192.168.1.0/24, 192.168.11.0/24, 192.168.13.0/24, 192.168.64.10/32
PersistentKeepalive = 30

[Peer]
PublicKey = mRjd9...
AllowedIps = 10.0.0.0/8, 192.168.64.11/32
PersistentKeepalive = 30

Client 1

# ip addr show wg0
33: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 192.168.64.10/24 scope global wg0
       valid_lft forever preferred_lft forever
# ip route | grep wg0
10.0.0.0/8 dev wg0 scope link
192.168.64.0/24 dev wg0 proto kernel scope link src 192.168.64.10
[Interface]
PrivateKey = <secret key>
ListenPort = 50001

[Peer]
PublicKey = 2VtQ/...
Endpoint = wg.example.com:50001
AllowedIps = 0.0.0.0/0, 192.168.64.1/32
PersistentKeepalive = 30

[Peer]
PublicKey = mRjd9...
AllowedIps = 10.0.0.0/8, 192.168.64.11/32
PersistentKeepalive = 30

Client 2

# ip addr show wg0
11: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 192.168.64.11/24 scope global wg0
       valid_lft forever preferred_lft forever
# ip route show | grep wg0
192.168.1.0/24 dev wg0 scope link
192.168.11.0/24 dev wg0 scope link
192.168.13.0/24 dev wg0 scope link
192.168.64.0/24 dev wg0 proto kernel scope link src 192.168.64.11
[Interface]
PrivateKey = <secret key>
ListenPort = 50001

[Peer]
PublicKey = 2VtQ/...
Endpoint = wg.example.com:50001
AllowedIps = 0.0.0.0/0, 192.168.64.1/32
PersistentKeepalive = 30

[Peer]
PublicKey = 1cML7...
AllowedIps = 192.168.1.0/24, 192.168.11.0/24, 192.168.13.0/24, 192.168.64.10/32
PersistentKeepalive = 30

Interface configuration script

The wg0 interface on the nodes is configured with the following script:

#!/bin/sh

dev=$1
addr=$2

ip link add $dev type wireguard
ip addr add $addr dev $dev

wg setconf $dev /etc/wireguard/$dev.conf

ip link set $dev  up

Best Answer

Let's follow what happens:

  • Client1's routing table tell it that 192.168.64.11 is reachable through wg0.
  • Client1's wg0 cryptokey-routing settings tell that 192.168.64.11 is reachable through 2nd Peer (with a key starting with mRjd9) aka Client2.
  • payload is ready to be tunneled to 2nd Peer's current endpoint... except there's no Endpoint defined to reach the 2nd Peer and as there was never traffic received in the other direction (for the very same reason) no endpoint exists. That's different for Server which on receiving the first packet from Client1 will set the current Client1's source address as the endpoint.

The same happens in the other direction from Client2 to Client1: there's no Endpoint defined and no traffic was received to have an endpoint set.

So the tunnel being currently incomplete, fails. wireguard sends the error EDESTADDRREQ/Destination address required when this happens. Just because it's more difficult to specify what is this other side, doesn't mean omitting it will magically make it work.

As both are NATed you get the usual NAT connectivity issues between two NATed devices.

To solve this you can either:

  • Use Server as a STUN-like server (maybe even running an actual STUN server, but outside of the tunnel) to synchronize the method for Client1 and Client2 to attempt UDP hole punching with WireGuard tunnels, assuming there's no Symmetric NATs or CG NAT in the way. That's beyond the scope of this WireGuard question to do this properly, but that should be what to favor to avoid needing Server for all traffic.

    Note that using 0.0.0.0/0 in AllowedIps on each client makes no sense as soon as there are two peers defined each with values in AllowedIps. 0.0.0.0/0 will be silently discarded from the cryptokey route in the end and should not be used. One IP address can resolve (ie: be cryptokey-routed) to exactly one Peer (or none) but never more than one.

  • or, as it appears was intended in OP, relay traffic through Server which requires to set Server as router on its wg0 interface (used both for ingress and egress) and change configuration on Client1 and Client2. There's no "dynamic cryptokeyrouting" protocol, the change must be done manually or with scripts on both clients (until some routing daemon able to do this for WireGuard appears). For example (here just using 0.0.0.0/0 instead of explicitly stating all the routes would have be fine, there's now only one actual Peer):

    • Client1

      [Interface]
      PrivateKey = <secret key>
      ListenPort = 50001
      
      [Peer]
      PublicKey = 2VtQ/...
      Endpoint = wg.example.com:50001
      AllowedIps = 192.168.64.1/32, 10.0.0.0/8, 192.168.64.11/32
      PersistentKeepalive = 30
      

      The part below becomes useless: Client1 never has Client2 as peer, only Server. But it can still be kept in the configuration:

      [Peer]
      PublicKey = mRjd9...
      AllowedIps = 
      PersistentKeepalive = 30
      
    • Client2

      [Interface]
      PrivateKey = <secret key>
      ListenPort = 50001
      
      [Peer]
      PublicKey = 2VtQ/...
      Endpoint = wg.example.com:50001
      AllowedIps = 192.168.64.1/32, 192.168.1.0/24, 192.168.11.0/24, 192.168.13.0/24, 192.168.64.10/32
      PersistentKeepalive = 30
      
      [Peer]
      PublicKey = 1cML7...
      AllowedIps = 
      PersistentKeepalive = 30
      
Related Topic