mirror of
https://github.com/slackhq/nebula.git
synced 2025-01-11 11:58:11 +00:00
e1af37e46d
* add calculated_remotes This setting allows us to "guess" what the remote might be for a host while we wait for the lighthouse response. For networks that hard designed with in mind, it can help speed up handshake performance, as well as improve resiliency in the case that all lighthouses are down. Example: lighthouse: # ... calculated_remotes: # For any Nebula IPs in 10.0.10.0/24, this will apply the mask and add # the calculated IP as an initial remote (while we wait for the response # from the lighthouse). Both CIDRs must have the same mask size. # For example, Nebula IP 10.0.10.123 will have a calculated remote of # 192.168.1.123 10.0.10.0/24: - mask: 192.168.1.0/24 port: 4242 * figure out what is up with this test * add test * better logic for sending handshakes Keep track of the last light of hosts we sent handshakes to. Only log handshake sent messages if the list has changed. Remove the test Test_NewHandshakeManagerTrigger because it is faulty and makes no sense. It relys on the fact that no handshake packets actually get sent, but with these changes we would send packets now (which it should!) * use atomic.Pointer * cleanup to make it clearer * fix typo in example
143 lines
3.5 KiB
Go
143 lines
3.5 KiB
Go
package nebula
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"strconv"
|
|
|
|
"github.com/slackhq/nebula/cidr"
|
|
"github.com/slackhq/nebula/config"
|
|
"github.com/slackhq/nebula/iputil"
|
|
)
|
|
|
|
// This allows us to "guess" what the remote might be for a host while we wait
|
|
// for the lighthouse response. See "lighthouse.calculated_remotes" in the
|
|
// example config file.
|
|
type calculatedRemote struct {
|
|
ipNet net.IPNet
|
|
maskIP iputil.VpnIp
|
|
mask iputil.VpnIp
|
|
port uint32
|
|
}
|
|
|
|
func newCalculatedRemote(ipNet *net.IPNet, port int) (*calculatedRemote, error) {
|
|
// Ensure this is an IPv4 mask that we expect
|
|
ones, bits := ipNet.Mask.Size()
|
|
if ones == 0 || bits != 32 {
|
|
return nil, fmt.Errorf("invalid mask: %v", ipNet)
|
|
}
|
|
if port < 0 || port > math.MaxUint16 {
|
|
return nil, fmt.Errorf("invalid port: %d", port)
|
|
}
|
|
|
|
return &calculatedRemote{
|
|
ipNet: *ipNet,
|
|
maskIP: iputil.Ip2VpnIp(ipNet.IP),
|
|
mask: iputil.Ip2VpnIp(ipNet.Mask),
|
|
port: uint32(port),
|
|
}, nil
|
|
}
|
|
|
|
func (c *calculatedRemote) String() string {
|
|
return fmt.Sprintf("CalculatedRemote(mask=%v port=%d)", c.ipNet, c.port)
|
|
}
|
|
|
|
func (c *calculatedRemote) Apply(ip iputil.VpnIp) *Ip4AndPort {
|
|
// Combine the masked bytes of the "mask" IP with the unmasked bytes
|
|
// of the overlay IP
|
|
masked := (c.maskIP & c.mask) | (ip & ^c.mask)
|
|
|
|
return &Ip4AndPort{Ip: uint32(masked), Port: c.port}
|
|
}
|
|
|
|
func NewCalculatedRemotesFromConfig(c *config.C, k string) (*cidr.Tree4, error) {
|
|
value := c.Get(k)
|
|
if value == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
calculatedRemotes := cidr.NewTree4()
|
|
|
|
rawMap, ok := value.(map[any]any)
|
|
if !ok {
|
|
return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value)
|
|
}
|
|
for rawKey, rawValue := range rawMap {
|
|
rawCIDR, ok := rawKey.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
|
|
}
|
|
|
|
_, ipNet, err := net.ParseCIDR(rawCIDR)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR)
|
|
}
|
|
|
|
entry, err := newCalculatedRemotesListFromConfig(rawValue)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("config '%s.%s': %w", k, rawCIDR, err)
|
|
}
|
|
|
|
calculatedRemotes.AddCIDR(ipNet, entry)
|
|
}
|
|
|
|
return calculatedRemotes, nil
|
|
}
|
|
|
|
func newCalculatedRemotesListFromConfig(raw any) ([]*calculatedRemote, error) {
|
|
rawList, ok := raw.([]any)
|
|
if !ok {
|
|
return nil, fmt.Errorf("calculated_remotes entry has invalid type: %T", raw)
|
|
}
|
|
|
|
var l []*calculatedRemote
|
|
for _, e := range rawList {
|
|
c, err := newCalculatedRemotesEntryFromConfig(e)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("calculated_remotes entry: %w", err)
|
|
}
|
|
l = append(l, c)
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
func newCalculatedRemotesEntryFromConfig(raw any) (*calculatedRemote, error) {
|
|
rawMap, ok := raw.(map[any]any)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid type: %T", raw)
|
|
}
|
|
|
|
rawValue := rawMap["mask"]
|
|
if rawValue == nil {
|
|
return nil, fmt.Errorf("missing mask: %v", rawMap)
|
|
}
|
|
rawMask, ok := rawValue.(string)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid mask (type %T): %v", rawValue, rawValue)
|
|
}
|
|
_, ipNet, err := net.ParseCIDR(rawMask)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid mask: %s", rawMask)
|
|
}
|
|
|
|
var port int
|
|
rawValue = rawMap["port"]
|
|
if rawValue == nil {
|
|
return nil, fmt.Errorf("missing port: %v", rawMap)
|
|
}
|
|
switch v := rawValue.(type) {
|
|
case int:
|
|
port = v
|
|
case string:
|
|
port, err = strconv.Atoi(v)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid port: %s: %w", v, err)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("invalid port (type %T): %v", rawValue, rawValue)
|
|
}
|
|
|
|
return newCalculatedRemote(ipNet, port)
|
|
}
|