Wireguard works even without setting a tunnel IP address, i.e. it's enough to set the AllowedIPs, endpoint addresses, private and public keys.
In the docs of OpnSense, there is the following warning:
Note: The tunnel address must be in CIDR notation and must be a unique IP and subnet for your network. [..] Do not use a tunnel address that is a /32 (IPv4) or a /128 (IPv6)
and pfSense has a likely explanation:
Note: Routes are not automatically created in the system routing table. Routes for networks other than the tunnel network itself must be configured separately using static or dynamic routes.
Searching on the internet does not yield many explanations:
- Reddit: Confusion about subnet masks
- Reddit: Help on /24 and /32 when using as a VPN Server
- Reddit: differences between /8, /16 etc... What they are used for?
The subnet does not seem to have any functionality, we did some testing:
- It's not related to local traffic routing, i.e. routing to a second connected peer works with and without a subnet which contains both peers.
- it's not related to "staying on the interface" vs passing through the kernel. In both cases we could control the traffic using firewall rules.
So, what is the purpose of the subnet mask in the tunnel ip?
The subnet mask does nothing WireGuard-specific.
WireGuard itself does not use or care about the subnet masks on its interface addresses (or even use or care about the addresses themselves). However, the network stack and other networking tools on the WireGuard host do care about the IP addresses and subnets registered for each network interface, and will use them to try to figure out if a particular interface is attached directly to a particular network.
Your observed behavior may vary based on the OS and particular networking tools you use (like OPNSense/pfSense and all their sundry plugins), but a lot of things like routing tables, firewall rules, ARP messages, neighbor tables, etc all may be generated automatically based on the IP addresses and subnets you have configured for each network interface. However, in many cases, you can still override these defaults through custom routing/firewall rules, kernel settings, network daemon configuration, etc.
For example, when you start up a WireGuard interface with the standard wg-quick script on Linux, that script will use the iproute2 tool to add each address and subnet you've configured for the interface. For each address it adds, iproute2 automatically adds a corresponding route to the main routing table to match the address's subnet. Using that script to start up an interface named
wg0
with an address of10.0.0.123/24
will result in iproute2 adding the following route to the main routing table:This is not a WireGuard thing, it's just the default behavior of some of the standard networking tools on Linux. You're free to delete that route and add other routes, or just use a different set of tools to set up the WireGuard interface. (But keep in mind that this or any other routes added to a WireGuard interface that don't have a corresponding
AllowedIPs
entry in the interface's own configuration will effectively become a blackhole.)The upshot is that for most cases, it's perfectly fine to use an address with a /32 or /128 subnet mask on a WireGuard interface. You can set up your own routing and firewall rules to send whatever traffic you want to that interface. But for more advanced cases -- particularly when you want to run tools on the interface like Proxy ARP, NDP Proxy, OSPF, etc (that are meant for use within local subnets) -- you can avoid having to fight those tools' default behavior by assigning the WireGuard interface and its virtual neighbors to a consistent logical subnet.