assign ipv6 address to each machine derived from their public keys

This commit is contained in:
Pavel Sviderski
2024-08-29 22:37:49 +10:00
parent eed2c8809c
commit 41098d00b8
8 changed files with 77 additions and 43 deletions
+8
View File
@@ -3,6 +3,7 @@ package network
import (
"net"
"net/netip"
"uncloud/internal/secret"
)
// MachineIP returns the IP address of the machine which is the first address in the subnet.
@@ -10,6 +11,13 @@ func MachineIP(subnet netip.Prefix) netip.Addr {
return subnet.Masked().Addr().Next()
}
// PeerIPv6 returns the IPv6 address of a peer derived from the first 14 bytes of its public key.
func PeerIPv6(publicKey secret.Secret) netip.Addr {
bytes := [16]byte{0xfd, 0xcc}
copy(bytes[2:], publicKey[:14])
return netip.AddrFrom16(bytes)
}
func prefixToIPNet(prefix netip.Prefix) net.IPNet {
return net.IPNet{
IP: prefix.Addr().AsSlice(),
+1 -1
View File
@@ -10,7 +10,7 @@ import (
const (
WireGuardInterfaceName = "uncloud"
WireGuardPort = 51820
// WireGuardKeepaliveInterval is sensible interval that works with a wide variety of firewalls is 25 seconds.
// WireGuardKeepaliveInterval is sensible interval that works with a wide variety of firewalls.
WireGuardKeepaliveInterval = 25 * time.Second
)
+23 -15
View File
@@ -11,6 +11,7 @@ import (
"golang.zx2c4.com/wireguard/wgctrl"
"log/slog"
"net/netip"
"slices"
"time"
)
@@ -101,11 +102,15 @@ func (n *WireGuardNetwork) Configure(config Config) error {
}
slog.Info("Configured WireGuard interface.", "name", n.link.Attrs().Name)
if err = n.updateSubnet(config.Subnet); err != nil {
machineIP := MachineIP(config.Subnet)
machineIPSubnet := netip.PrefixFrom(machineIP, config.Subnet.Bits())
machineIPv6 := netip.PrefixFrom(PeerIPv6(config.PublicKey), 128)
addrs := []netip.Prefix{machineIPSubnet, machineIPv6}
if err = n.updateAddresses(addrs); err != nil {
return err
}
slog.Info("Updated the subnet of the WireGuard interface.",
"name", n.link.Attrs().Name, "subnet", config.Subnet)
slog.Info("Updated addresses of the WireGuard interface.",
"name", n.link.Attrs().Name, "addrs", addrs)
// Bring the WireGuard interface up if it's not already up.
if n.link.Attrs().Flags&unix.IFF_UP != unix.IFF_UP {
@@ -123,27 +128,30 @@ func (n *WireGuardNetwork) Configure(config Config) error {
return nil
}
// updateSubnet assigns the subnet and the first IP address in it to the WireGuard interface.
// updateAddresses assigns addresses to the WireGuard interface and removes old ones.
// It also removes any other addresses that have been added out of band.
func (n *WireGuardNetwork) updateSubnet(subnet netip.Prefix) error {
machineIP := MachineIP(subnet)
ipSubnet := prefixToIPNet(netip.PrefixFrom(machineIP, subnet.Bits()))
if err := netlink.AddrAdd(n.link, &netlink.Addr{IPNet: &ipSubnet}); err != nil {
if !errors.Is(err, unix.EEXIST) {
return fmt.Errorf("add subnet address to WireGuard link %q: %w", n.link.Attrs().Name, err)
func (n *WireGuardNetwork) updateAddresses(addrs []netip.Prefix) error {
for _, addr := range addrs {
ipNet := prefixToIPNet(addr)
if err := netlink.AddrAdd(n.link, &netlink.Addr{IPNet: &ipNet}); err != nil {
if !errors.Is(err, unix.EEXIST) {
return fmt.Errorf("add subnet address to WireGuard link %q: %w", n.link.Attrs().Name, err)
}
}
}
// Remove the old subnet address if it has changed and remove any other addresses that have been added out of band.
// Remove the old addresses or any other addresses that have been added out of band.
linkAddrs, err := netlink.AddrList(n.link, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("list addresses on WireGuard link %q: %w", n.link.Attrs().Name, err)
}
for _, addr := range linkAddrs {
if addr.IPNet.String() == ipSubnet.String() {
for _, linkAddr := range linkAddrs {
if slices.ContainsFunc(addrs, func(a netip.Prefix) bool {
return linkAddr.IPNet.String() == a.String()
}) {
continue
}
if err = netlink.AddrDel(n.link, &addr); err != nil {
return fmt.Errorf("remove address %q from WireGuard link %q: %w", addr.IPNet, n.link.Attrs().Name, err)
if err = netlink.AddrDel(n.link, &linkAddr); err != nil {
return fmt.Errorf("remove address %q from WireGuard link %q: %w", linkAddr.IPNet, n.link.Attrs().Name, err)
}
}
return nil