With this configuration:
[Interface]
...
Address = 172.16.110.116/28,172.16.111.21/28
[Peer]
...
AllowedIPs = 172.16.110.112/28
[Peer]
...
AllowedIPs = 172.16.111.16/28
NetworkManager currently creates the following routes
(1) 172.16.110.112/28 dev wg0 proto static scope link metric 50 <-- peer route
(2) 172.16.110.112/28 dev wg0 proto kernel scope link src 172.16.110.116 metric 50 <-- prefix route
(3) 172.16.111.16/28 dev wg0 proto static scope link metric 50 <-- peer route
(4) 172.16.111.16/28 dev wg0 proto kernel scope link src 172.16.111.21 metric 50 <-- prefix route
If we try to reach a host in the second peer subnet, route (4)
matches. Route (4) doesn't specify a source IP and so the kernel will
use the first IP set on the interface (172.16.110.116), which is the
wrong one.
# ip route get 172.16.111.17
172.16.111.17 dev wg0 src 172.16.110.116 uid 0
To fix this problem, if the AllowedIP subnet is already reachable on
the interface via the prefix route of a static IP address, we should
skip adding the peer route.
wg-quick does something similar here:
https://git.zx2c4.com/wireguard-tools/tree/src/wg-quick/linux.bash?h=v1.0.20250521#n177
The condition in wg-quick is a bit different because it checks that no
duplicate route exists on the interface. We can't do exactly the same
because in NMDeviceWireGuard we don't have visibility on all the
platform routes.
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1790https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2254