Add support for multiple IP prefixes

This commit is contained in:
Csaba Sarkadi 2022-01-16 14:16:59 +01:00
parent 3a3aecb774
commit 1a6e5d8770
18 changed files with 423 additions and 257 deletions

View file

@ -1,6 +1,7 @@
package headscale
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
@ -23,6 +24,7 @@ const (
errMachineNotFound = Error("machine not found")
errMachineAlreadyRegistered = Error("machine already registered")
errMachineRouteIsNotAvailable = Error("route is not available on machine")
errMachineAddressesInvalid = Error("failed to parse machine addresses")
)
// Machine is a Headscale client.
@ -31,7 +33,7 @@ type Machine struct {
MachineKey string `gorm:"type:varchar(64);unique_index"`
NodeKey string
DiscoKey string
IPAddress string
IPAddresses MachineAddresses
Name string
NamespaceID uint
Namespace Namespace `gorm:"foreignKey:NamespaceID"`
@ -64,6 +66,47 @@ func (machine Machine) isRegistered() bool {
return machine.Registered
}
type MachineAddresses []netaddr.IP
func (ma MachineAddresses) ToStringSlice() []string {
strSlice := make([]string, 0, len(ma))
for _, addr := range ma {
strSlice = append(strSlice, addr.String())
}
return strSlice
}
func (ma *MachineAddresses) Scan(destination interface{}) error {
switch value := destination.(type) {
case string:
addresses := strings.Split(value, ",")
*ma = (*ma)[:0]
for _, addr := range addresses {
if len(addr) < 1 {
continue
}
parsed, err := netaddr.ParseIP(addr)
if err != nil {
return err
}
*ma = append(*ma, parsed)
}
return nil
default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination)
}
}
// Value return json value, implement driver.Valuer interface.
func (ma MachineAddresses) Value() (driver.Value, error) {
addresses := strings.Join(ma.ToStringSlice(), ",")
return addresses, nil
}
// isExpired returns whether the machine registration has expired.
func (machine Machine) isExpired() bool {
// If Expiry is not set, the client has not indicated that
@ -470,22 +513,12 @@ func (machine Machine) toNode(
}
addrs := []netaddr.IPPrefix{}
nodeAddr, err := netaddr.ParseIP(m.IPAddresses)
if err != nil {
log.Trace().
Caller().
Str("ip", machine.IPAddresses).
Msgf("Failed to parse machine IP: %s", machine.IPAddresses)
return nil, err
for _, machineAddress := range machine.IPAddresses {
ip := netaddr.IPPrefixFrom(machineAddress, machineAddress.BitLen())
addrs = append(addrs, ip)
}
ip := netaddr.IPPrefixFrom(nodeAddr, nodeAddr.BitLen())
addrs = append(addrs, ip)
allowedIPs := []netaddr.IPPrefix{}
allowedIPs = append(
allowedIPs,
ip,
) // we append the node own IP, as it is required by the clients
allowedIPs := append([]netaddr.IPPrefix{}, addrs...) // we append the node own IP, as it is required by the clients
if includeRoutes {
routesStr := []string{}
@ -592,11 +625,11 @@ func (machine *Machine) toProto() *v1.Machine {
Id: machine.ID,
MachineKey: machine.MachineKey,
NodeKey: machine.NodeKey,
DiscoKey: machine.DiscoKey,
IpAddress: machine.IPAddress,
Name: machine.Name,
Namespace: machine.Namespace.toProto(),
NodeKey: machine.NodeKey,
DiscoKey: machine.DiscoKey,
IpAddresses: machine.IPAddresses.ToStringSlice(),
Name: machine.Name,
Namespace: machine.Namespace.toProto(),
Registered: machine.Registered,
@ -695,7 +728,7 @@ func (h *Headscale) RegisterMachine(
return nil, err
}
ip, err := h.getAvailableIP()
ips, err := h.getAvailableIPs()
if err != nil {
log.Error().
Caller().
@ -709,10 +742,10 @@ func (h *Headscale) RegisterMachine(
log.Trace().
Caller().
Str("machine", machine.Name).
Str("ip", ip.String()).
Str("ip", strings.Join(ips.ToStringSlice(), ",")).
Msg("Found IP for host")
machine.IPAddress = ip.String()
machine.IPAddresses = ips
machine.NamespaceID = namespace.ID
machine.Registered = true
machine.RegisterMethod = RegisterMethodCLI
@ -722,7 +755,7 @@ func (h *Headscale) RegisterMachine(
log.Trace().
Caller().
Str("machine", machine.Name).
Str("ip", ip.String()).
Str("ip", strings.Join(ips.ToStringSlice(), ",")).
Msg("Machine registered with the database")
return machine, nil