You can still use two ipv6 addresses for the server side of wireguard (like you do with OpenVPN). Sticky sockets should not be needed. Address 1 is assigned to physical interface (say em0), and Address 2 is assigned to the Wireguard
tun()interface (say wg-server0). The key here is not to have both ipv6 addresses assigned to physical interfaces, and control your subnet mask/CIDR. On the server side config, use AllowedIPs=2001:db8::2/128 for the [Peer] (use /128 so you do not have a conflict with the other ipv6 address if they are both in the same /64 (likely).
Inside the tunnel address family does not have to match outside the tunnel address family with wireguard. It will happily tunnel ipv6 in ipv4 or ipv4 in ipv6, etc. In this example.com scenario, if this dns name has both A and AAAA records, the outside the tunnel comms to the other end will work just fine regardless of address family as long as there is end-to-end connectivity for UDP port 51820 to the server for both address families. Just ensure that you have UDP port 51820 open in your firewall for both your ipv4 remote endpoint , and your ipv6 remote endpoint in addition to NAT port forwarding rules if required by your network setup. Wireguard always does a ipv4/ipv6 wildcard bind to all interfaces (technically one socket per address family) for the ListenPort= configured. It will happily switch back and forth between interfaces and addresses depending on what address family is used to connect inbound outside the tunnel interface. In a normal internet facing server, this "bind everywhere" would be a concern, but since wireguard has a strong crypto-based security model, the risk is minimal, and you can always add firewall rules to add layers of protection as desired. As a side note, the Android and Windows builds of wireguard do not re-resolve dns names as aggressively as I would like, and dual-stack dns names for Endpoint configs frequently do not result in expected or desired behaviors. In these cases, it is always easier to use an ip address for initial troubleshooting to avoid looking at multiple problems simultaneously.
In addition, it is easier to start troubleshooting routing and comms issues if you start out with non-default routes (routes only for inside-tunnel ip ranges. The moment you use ::/0 or 0.0.0.0/0 in your AllowedIPs block, this can kill existing ssh connections, and disrupt other things since wg-quick/wg does both routes and allow ACLS based upon this single configuration line. There are tricks that allow you to impact the ACL list without impacting the routes by using PostUp configuration lines. The included wg-quick(8) bash script swallows most error conditions when adding/deleting routing entries by using the -q (quiet) command line (I hate this for troubleshooting reasons). This is some of the logic I am trying to find more elegant solutions around and create a PR for upstream. I also like to add a PostUp command to ping the other side of the tunnel so I can A) see traffic immediately (or not), and B) wireguard has completed the handshake right away so that is visible to wg(8) show command under "latest handshake" / "transfer" status.
Note: ListenPort= and Endpoint= configuration lines are for outside the tunnel, Address= and AllowedIPs= configuration lines are for inside the tunnel. AllowedIPs does two things: Routes and ACLs.
/etc/wireguard/Postup-client-example.conf
[Interface]
Privatekey = ABCD...
Address = 2001:db8::2/64, 10.0.0.2/24
# Allow any address coming from the other side of the tunnel (ipv4+ipv6)
PostUp = wg set Postup-client-example peer EFGH allow-ips ::/0,0.0.0.0/0
PostUp = ping6 -c 2 2001:db8::1
[Peer]
PublicKey = EFGH...
Endpoint = dualstack.example.com:51820
# Use /128 (ipv6) or /32 (ipv4) if all you need is point-to-point tunnel.
AllowedIPs = 2001:db8::/64, 10.0.0.0/24
Confirm your listening ports on the server:
#sockstat -l | grep -i wireguard
root wireguard- 83188 8 stream /var/run/wireguard/server0.sock
root wireguard- 83188 11 udp4 *:51820 *:*
root wireguard- 83188 12 udp6 *:51820 *:*
/etc/wireguard/wg-server0.conf (configuration file on server peer side)
# Remote endpoint with listen socket on pre-defined UDP port (aka server)
[Interface]
PrivateKey = WERT...
Address = 10.0.0.1/24, 2001:db8::1/64
ListenPort = 51820
[Peer]
PublicKey = YUIO...
AllowedIPs = 2001:db8::2/128, 10.0.0.2/32
/etc/wireguard/wg-client0.conf (configuration file on client peer side)
# Local endpoint with listen socket on dynamic UDP port
[Interface]
PrivateKey = HJKL...
Address = 2001:db8::2/64, 10.0.0.2/24
[Peer]
PublicKey = VBNM...
Endpoint = dualstack.example.com:51820
AllowedIPs = 2001:db8::/64, 10.0.0.0/24
Here is the quick and dirty patch to support ipv6 for sockarg in kernel ipfw:
I think this feature for ipfw sockarg on ipv6 was just disabled due to lack of testing pre-commit, but it seems to work in my limited experience thus far. Obviously, requires compiling a custom kernel to apply. YMMV.
Code:
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c
index 34ce208ee5f..2c31199bb0c 100644
--- a/sys/netpfil/ipfw/ip_fw2.c
+++ b/sys/netpfil/ipfw/ip_fw2.c
@@ -2557,8 +2557,6 @@ do { \
struct inpcb *inp = args->inp;
struct inpcbinfo *pi;
- if (is_ipv6) /* XXX can we remove this ? */
- break;
if (proto == IPPROTO_TCP)
pi = &V_tcbinfo;
Apologies in advance for the rambling stream of consciousness, and any typos.