2019-11-19 10:00:20 -07:00
|
|
|
package nebula
|
|
|
|
|
|
|
|
import (
|
2021-11-02 12:14:26 -06:00
|
|
|
"context"
|
2019-11-19 10:00:20 -07:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"sync"
|
2021-03-05 19:18:33 -07:00
|
|
|
"sync/atomic"
|
2019-11-19 10:00:20 -07:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/rcrowley/go-metrics"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/slackhq/nebula/cert"
|
2021-11-03 19:54:04 -06:00
|
|
|
"github.com/slackhq/nebula/cidr"
|
|
|
|
"github.com/slackhq/nebula/header"
|
|
|
|
"github.com/slackhq/nebula/iputil"
|
|
|
|
"github.com/slackhq/nebula/udp"
|
2019-11-19 10:00:20 -07:00
|
|
|
)
|
|
|
|
|
2022-10-31 11:37:41 -06:00
|
|
|
// const ProbeLen = 100
|
2019-11-19 10:00:20 -07:00
|
|
|
const PromoteEvery = 1000
|
2021-04-14 12:50:09 -06:00
|
|
|
const ReQueryEvery = 5000
|
2019-11-19 10:00:20 -07:00
|
|
|
const MaxRemotes = 10
|
|
|
|
|
2023-03-13 11:35:14 -06:00
|
|
|
// MaxHostInfosPerVpnIp is the max number of hostinfos we will track for a given vpn ip
|
|
|
|
// 5 allows for an initial handshake and each host pair re-handshaking twice
|
|
|
|
const MaxHostInfosPerVpnIp = 5
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
// How long we should prevent roaming back to the previous IP.
|
|
|
|
// This helps prevent flapping due to packets already in flight
|
2021-03-01 09:14:34 -07:00
|
|
|
const RoamingSuppressSeconds = 2
|
2019-11-19 10:00:20 -07:00
|
|
|
|
2022-06-21 12:35:23 -06:00
|
|
|
const (
|
|
|
|
Requested = iota
|
|
|
|
Established
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
Unknowntype = iota
|
|
|
|
ForwardingType
|
|
|
|
TerminalType
|
|
|
|
)
|
|
|
|
|
|
|
|
type Relay struct {
|
|
|
|
Type int
|
|
|
|
State int
|
|
|
|
LocalIndex uint32
|
|
|
|
RemoteIndex uint32
|
|
|
|
PeerIp iputil.VpnIp
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
type HostMap struct {
|
|
|
|
sync.RWMutex //Because we concurrently read and write to our maps
|
|
|
|
name string
|
|
|
|
Indexes map[uint32]*HostInfo
|
2022-06-21 12:35:23 -06:00
|
|
|
Relays map[uint32]*HostInfo // Maps a Relay IDX to a Relay HostInfo object
|
2020-11-23 12:51:16 -07:00
|
|
|
RemoteIndexes map[uint32]*HostInfo
|
2021-11-03 19:54:04 -06:00
|
|
|
Hosts map[iputil.VpnIp]*HostInfo
|
2019-11-19 10:00:20 -07:00
|
|
|
preferredRanges []*net.IPNet
|
|
|
|
vpnCIDR *net.IPNet
|
2020-06-26 11:45:48 -06:00
|
|
|
metricsEnabled bool
|
2021-03-26 08:46:30 -06:00
|
|
|
l *logrus.Logger
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-21 12:35:23 -06:00
|
|
|
type RelayState struct {
|
|
|
|
sync.RWMutex
|
|
|
|
|
|
|
|
relays map[iputil.VpnIp]struct{} // Set of VpnIp's of Hosts to use as relays to access this peer
|
|
|
|
relayForByIp map[iputil.VpnIp]*Relay // Maps VpnIps of peers for which this HostInfo is a relay to some Relay info
|
|
|
|
relayForByIdx map[uint32]*Relay // Maps a local index to some Relay info
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) DeleteRelay(ip iputil.VpnIp) {
|
|
|
|
rs.Lock()
|
|
|
|
defer rs.Unlock()
|
|
|
|
delete(rs.relays, ip)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) GetRelayForByIp(ip iputil.VpnIp) (*Relay, bool) {
|
|
|
|
rs.RLock()
|
|
|
|
defer rs.RUnlock()
|
|
|
|
r, ok := rs.relayForByIp[ip]
|
|
|
|
return r, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) InsertRelayTo(ip iputil.VpnIp) {
|
|
|
|
rs.Lock()
|
|
|
|
defer rs.Unlock()
|
|
|
|
rs.relays[ip] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) CopyRelayIps() []iputil.VpnIp {
|
|
|
|
rs.RLock()
|
|
|
|
defer rs.RUnlock()
|
|
|
|
ret := make([]iputil.VpnIp, 0, len(rs.relays))
|
|
|
|
for ip := range rs.relays {
|
|
|
|
ret = append(ret, ip)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) CopyRelayForIps() []iputil.VpnIp {
|
|
|
|
rs.RLock()
|
|
|
|
defer rs.RUnlock()
|
|
|
|
currentRelays := make([]iputil.VpnIp, 0, len(rs.relayForByIp))
|
|
|
|
for relayIp := range rs.relayForByIp {
|
|
|
|
currentRelays = append(currentRelays, relayIp)
|
|
|
|
}
|
|
|
|
return currentRelays
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) CopyRelayForIdxs() []uint32 {
|
|
|
|
rs.RLock()
|
|
|
|
defer rs.RUnlock()
|
|
|
|
ret := make([]uint32, 0, len(rs.relayForByIdx))
|
|
|
|
for i := range rs.relayForByIdx {
|
|
|
|
ret = append(ret, i)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) RemoveRelay(localIdx uint32) (iputil.VpnIp, bool) {
|
|
|
|
rs.Lock()
|
|
|
|
defer rs.Unlock()
|
|
|
|
relay, ok := rs.relayForByIdx[localIdx]
|
|
|
|
if !ok {
|
|
|
|
return iputil.VpnIp(0), false
|
|
|
|
}
|
|
|
|
delete(rs.relayForByIdx, localIdx)
|
|
|
|
delete(rs.relayForByIp, relay.PeerIp)
|
|
|
|
return relay.PeerIp, true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) QueryRelayForByIp(vpnIp iputil.VpnIp) (*Relay, bool) {
|
|
|
|
rs.RLock()
|
|
|
|
defer rs.RUnlock()
|
|
|
|
r, ok := rs.relayForByIp[vpnIp]
|
|
|
|
return r, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rs *RelayState) QueryRelayForByIdx(idx uint32) (*Relay, bool) {
|
|
|
|
rs.RLock()
|
|
|
|
defer rs.RUnlock()
|
|
|
|
r, ok := rs.relayForByIdx[idx]
|
|
|
|
return r, ok
|
|
|
|
}
|
|
|
|
func (rs *RelayState) InsertRelay(ip iputil.VpnIp, idx uint32, r *Relay) {
|
|
|
|
rs.Lock()
|
|
|
|
defer rs.Unlock()
|
|
|
|
rs.relayForByIp[ip] = r
|
|
|
|
rs.relayForByIdx[idx] = r
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
type HostInfo struct {
|
2021-03-05 19:18:33 -07:00
|
|
|
sync.RWMutex
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
remote *udp.Addr
|
2021-04-14 12:50:09 -06:00
|
|
|
remotes *RemoteList
|
2022-10-31 11:37:41 -06:00
|
|
|
promoteCounter atomic.Uint32
|
2019-11-19 10:00:20 -07:00
|
|
|
ConnectionState *ConnectionState
|
2021-04-14 12:50:09 -06:00
|
|
|
handshakeStart time.Time //todo: this an entry in the handshake manager
|
|
|
|
HandshakeReady bool //todo: being in the manager means you are ready
|
|
|
|
HandshakeCounter int //todo: another handshake manager entry
|
|
|
|
HandshakeComplete bool //todo: this should go away in favor of ConnectionState.ready
|
|
|
|
HandshakePacket map[uint8][]byte //todo: this is other handshake manager entry
|
|
|
|
packetStore []*cachedPacket //todo: this is other handshake manager entry
|
2019-11-19 10:00:20 -07:00
|
|
|
remoteIndexId uint32
|
|
|
|
localIndexId uint32
|
2021-11-03 19:54:04 -06:00
|
|
|
vpnIp iputil.VpnIp
|
2019-11-19 10:00:20 -07:00
|
|
|
recvError int
|
2021-11-03 19:54:04 -06:00
|
|
|
remoteCidr *cidr.Tree4
|
2022-06-21 12:35:23 -06:00
|
|
|
relayState RelayState
|
2019-11-19 10:00:20 -07:00
|
|
|
|
2021-03-01 18:06:01 -07:00
|
|
|
// lastRebindCount is the other side of Interface.rebindCount, if these values don't match then we need to ask LH
|
|
|
|
// for a punch from the remote end of this tunnel. The goal being to prime their conntrack for our traffic just like
|
|
|
|
// with a handshake
|
|
|
|
lastRebindCount int8
|
|
|
|
|
2021-04-27 20:15:34 -06:00
|
|
|
// lastHandshakeTime records the time the remote side told us about at the stage when the handshake was completed locally
|
|
|
|
// Stage 1 packet will contain it if I am a responder, stage 2 packet if I am an initiator
|
|
|
|
// This is used to avoid an attack where a handshake packet is replayed after some time
|
|
|
|
lastHandshakeTime uint64
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
lastRoam time.Time
|
2021-11-03 19:54:04 -06:00
|
|
|
lastRoamRemote *udp.Addr
|
2023-03-13 11:35:14 -06:00
|
|
|
|
|
|
|
// Used to track other hostinfos for this vpn ip since only 1 can be primary
|
|
|
|
// Synchronised via hostmap lock and not the hostinfo lock.
|
|
|
|
next, prev *HostInfo
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-21 12:35:23 -06:00
|
|
|
type ViaSender struct {
|
|
|
|
relayHI *HostInfo // relayHI is the host info object of the relay
|
|
|
|
remoteIdx uint32 // remoteIdx is the index included in the header of the received packet
|
|
|
|
relay *Relay // relay contains the rest of the relay information, including the PeerIP of the host trying to communicate with us.
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
type cachedPacket struct {
|
2021-11-03 19:54:04 -06:00
|
|
|
messageType header.MessageType
|
|
|
|
messageSubType header.MessageSubType
|
2019-11-19 10:00:20 -07:00
|
|
|
callback packetCallback
|
|
|
|
packet []byte
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
type packetCallback func(t header.MessageType, st header.MessageSubType, h *HostInfo, p, nb, out []byte)
|
2019-11-19 10:00:20 -07:00
|
|
|
|
2021-04-27 20:23:18 -06:00
|
|
|
type cachedPacketMetrics struct {
|
|
|
|
sent metrics.Counter
|
|
|
|
dropped metrics.Counter
|
|
|
|
}
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
func NewHostMap(l *logrus.Logger, name string, vpnCIDR *net.IPNet, preferredRanges []*net.IPNet) *HostMap {
|
2021-11-03 19:54:04 -06:00
|
|
|
h := map[iputil.VpnIp]*HostInfo{}
|
2019-11-19 10:00:20 -07:00
|
|
|
i := map[uint32]*HostInfo{}
|
2020-11-23 12:51:16 -07:00
|
|
|
r := map[uint32]*HostInfo{}
|
2022-06-21 12:35:23 -06:00
|
|
|
relays := map[uint32]*HostInfo{}
|
2019-11-19 10:00:20 -07:00
|
|
|
m := HostMap{
|
|
|
|
name: name,
|
|
|
|
Indexes: i,
|
2022-06-21 12:35:23 -06:00
|
|
|
Relays: relays,
|
2020-11-23 12:51:16 -07:00
|
|
|
RemoteIndexes: r,
|
2019-11-19 10:00:20 -07:00
|
|
|
Hosts: h,
|
|
|
|
preferredRanges: preferredRanges,
|
|
|
|
vpnCIDR: vpnCIDR,
|
2021-03-26 08:46:30 -06:00
|
|
|
l: l,
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
return &m
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateStats takes a name and reports host and index counts to the stats collection system
|
|
|
|
func (hm *HostMap) EmitStats(name string) {
|
|
|
|
hm.RLock()
|
|
|
|
hostLen := len(hm.Hosts)
|
|
|
|
indexLen := len(hm.Indexes)
|
2020-11-23 12:51:16 -07:00
|
|
|
remoteIndexLen := len(hm.RemoteIndexes)
|
2022-06-21 12:35:23 -06:00
|
|
|
relaysLen := len(hm.Relays)
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RUnlock()
|
|
|
|
|
|
|
|
metrics.GetOrRegisterGauge("hostmap."+name+".hosts", nil).Update(int64(hostLen))
|
|
|
|
metrics.GetOrRegisterGauge("hostmap."+name+".indexes", nil).Update(int64(indexLen))
|
2020-11-23 12:51:16 -07:00
|
|
|
metrics.GetOrRegisterGauge("hostmap."+name+".remoteIndexes", nil).Update(int64(remoteIndexLen))
|
2022-06-21 12:35:23 -06:00
|
|
|
metrics.GetOrRegisterGauge("hostmap."+name+".relayIndexes", nil).Update(int64(relaysLen))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hm *HostMap) RemoveRelay(localIdx uint32) {
|
|
|
|
hm.Lock()
|
|
|
|
hiRelay, ok := hm.Relays[localIdx]
|
|
|
|
if !ok {
|
|
|
|
hm.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
delete(hm.Relays, localIdx)
|
|
|
|
hm.Unlock()
|
|
|
|
ip, ok := hiRelay.relayState.RemoveRelay(localIdx)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
hiPeer, err := hm.QueryVpnIp(ip)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var otherPeerIdx uint32
|
|
|
|
hiPeer.relayState.DeleteRelay(hiRelay.vpnIp)
|
|
|
|
relay, ok := hiPeer.relayState.GetRelayForByIp(hiRelay.vpnIp)
|
|
|
|
if ok {
|
|
|
|
otherPeerIdx = relay.LocalIndex
|
|
|
|
}
|
|
|
|
// I am a relaying host. I need to remove the other relay, too.
|
|
|
|
hm.RemoveRelay(otherPeerIdx)
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) GetIndexByVpnIp(vpnIp iputil.VpnIp) (uint32, error) {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RLock()
|
2021-11-03 19:54:04 -06:00
|
|
|
if i, ok := hm.Hosts[vpnIp]; ok {
|
2019-11-19 10:00:20 -07:00
|
|
|
index := i.localIndexId
|
|
|
|
hm.RUnlock()
|
|
|
|
return index, nil
|
|
|
|
}
|
|
|
|
hm.RUnlock()
|
|
|
|
return 0, errors.New("vpn IP not found")
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) Add(ip iputil.VpnIp, hostinfo *HostInfo) {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.Lock()
|
|
|
|
hm.Hosts[ip] = hostinfo
|
|
|
|
hm.Unlock()
|
|
|
|
}
|
|
|
|
|
2021-11-08 12:46:22 -07:00
|
|
|
func (hm *HostMap) AddVpnIp(vpnIp iputil.VpnIp, init func(hostinfo *HostInfo)) (hostinfo *HostInfo, created bool) {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RLock()
|
2021-11-08 12:46:22 -07:00
|
|
|
if h, ok := hm.Hosts[vpnIp]; !ok {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RUnlock()
|
|
|
|
h = &HostInfo{
|
2021-11-03 19:54:04 -06:00
|
|
|
vpnIp: vpnIp,
|
2019-11-19 10:00:20 -07:00
|
|
|
HandshakePacket: make(map[uint8][]byte, 0),
|
2022-06-21 12:35:23 -06:00
|
|
|
relayState: RelayState{
|
|
|
|
relays: map[iputil.VpnIp]struct{}{},
|
|
|
|
relayForByIp: map[iputil.VpnIp]*Relay{},
|
|
|
|
relayForByIdx: map[uint32]*Relay{},
|
|
|
|
},
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
2021-11-08 12:46:22 -07:00
|
|
|
if init != nil {
|
|
|
|
init(h)
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.Lock()
|
2021-11-03 19:54:04 -06:00
|
|
|
hm.Hosts[vpnIp] = h
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.Unlock()
|
2021-11-08 12:46:22 -07:00
|
|
|
return h, true
|
2019-11-19 10:00:20 -07:00
|
|
|
} else {
|
|
|
|
hm.RUnlock()
|
2021-11-08 12:46:22 -07:00
|
|
|
return h, false
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) DeleteVpnIp(vpnIp iputil.VpnIp) {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.Lock()
|
2021-11-03 19:54:04 -06:00
|
|
|
delete(hm.Hosts, vpnIp)
|
2019-11-19 10:00:20 -07:00
|
|
|
if len(hm.Hosts) == 0 {
|
2021-11-03 19:54:04 -06:00
|
|
|
hm.Hosts = map[iputil.VpnIp]*HostInfo{}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
hm.Unlock()
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level >= logrus.DebugLevel {
|
2021-11-03 19:54:04 -06:00
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "vpnIp": vpnIp, "mapTotalSize": len(hm.Hosts)}).
|
2019-11-19 10:00:20 -07:00
|
|
|
Debug("Hostmap vpnIp deleted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-23 12:51:16 -07:00
|
|
|
// Only used by pendingHostMap when the remote index is not initially known
|
|
|
|
func (hm *HostMap) addRemoteIndexHostInfo(index uint32, h *HostInfo) {
|
|
|
|
hm.Lock()
|
|
|
|
h.remoteIndexId = index
|
|
|
|
hm.RemoteIndexes[index] = h
|
|
|
|
hm.Unlock()
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level > logrus.DebugLevel {
|
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "indexNumber": index, "mapTotalSize": len(hm.Indexes),
|
2021-11-03 19:54:04 -06:00
|
|
|
"hostinfo": m{"existing": true, "localIndexId": h.localIndexId, "hostId": h.vpnIp}}).
|
2020-11-23 12:51:16 -07:00
|
|
|
Debug("Hostmap remoteIndex added")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) AddVpnIpHostInfo(vpnIp iputil.VpnIp, h *HostInfo) {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.Lock()
|
2021-11-03 19:54:04 -06:00
|
|
|
h.vpnIp = vpnIp
|
|
|
|
hm.Hosts[vpnIp] = h
|
2020-11-23 12:51:16 -07:00
|
|
|
hm.Indexes[h.localIndexId] = h
|
|
|
|
hm.RemoteIndexes[h.remoteIndexId] = h
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.Unlock()
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level > logrus.DebugLevel {
|
2021-11-03 19:54:04 -06:00
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "vpnIp": vpnIp, "mapTotalSize": len(hm.Hosts),
|
|
|
|
"hostinfo": m{"existing": true, "localIndexId": h.localIndexId, "vpnIp": h.vpnIp}}).
|
2019-11-19 10:00:20 -07:00
|
|
|
Debug("Hostmap vpnIp added")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 10:40:46 -07:00
|
|
|
// This is only called in pendingHostmap, to cleanup an inbound handshake
|
2019-11-19 10:00:20 -07:00
|
|
|
func (hm *HostMap) DeleteIndex(index uint32) {
|
|
|
|
hm.Lock()
|
2021-03-01 10:40:46 -07:00
|
|
|
hostinfo, ok := hm.Indexes[index]
|
|
|
|
if ok {
|
|
|
|
delete(hm.Indexes, index)
|
|
|
|
delete(hm.RemoteIndexes, hostinfo.remoteIndexId)
|
|
|
|
|
|
|
|
// Check if we have an entry under hostId that matches the same hostinfo
|
|
|
|
// instance. Clean it up as well if we do.
|
2021-11-03 19:54:04 -06:00
|
|
|
hostinfo2, ok := hm.Hosts[hostinfo.vpnIp]
|
2021-03-01 10:40:46 -07:00
|
|
|
if ok && hostinfo2 == hostinfo {
|
2021-11-03 19:54:04 -06:00
|
|
|
delete(hm.Hosts, hostinfo.vpnIp)
|
2021-03-01 10:40:46 -07:00
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
hm.Unlock()
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level >= logrus.DebugLevel {
|
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "indexNumber": index, "mapTotalSize": len(hm.Indexes)}).
|
2019-11-19 10:00:20 -07:00
|
|
|
Debug("Hostmap index deleted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 10:40:46 -07:00
|
|
|
// This is used to cleanup on recv_error
|
|
|
|
func (hm *HostMap) DeleteReverseIndex(index uint32) {
|
|
|
|
hm.Lock()
|
|
|
|
hostinfo, ok := hm.RemoteIndexes[index]
|
|
|
|
if ok {
|
|
|
|
delete(hm.Indexes, hostinfo.localIndexId)
|
|
|
|
delete(hm.RemoteIndexes, index)
|
|
|
|
|
|
|
|
// Check if we have an entry under hostId that matches the same hostinfo
|
|
|
|
// instance. Clean it up as well if we do (they might not match in pendingHostmap)
|
|
|
|
var hostinfo2 *HostInfo
|
2021-11-03 19:54:04 -06:00
|
|
|
hostinfo2, ok = hm.Hosts[hostinfo.vpnIp]
|
2021-03-01 10:40:46 -07:00
|
|
|
if ok && hostinfo2 == hostinfo {
|
2021-11-03 19:54:04 -06:00
|
|
|
delete(hm.Hosts, hostinfo.vpnIp)
|
2021-03-01 10:40:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
hm.Unlock()
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level >= logrus.DebugLevel {
|
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "indexNumber": index, "mapTotalSize": len(hm.Indexes)}).
|
2021-03-01 10:40:46 -07:00
|
|
|
Debug("Hostmap remote index deleted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-13 11:35:14 -06:00
|
|
|
// DeleteHostInfo will fully unlink the hostinfo and return true if it was the final hostinfo for this vpn ip
|
|
|
|
func (hm *HostMap) DeleteHostInfo(hostinfo *HostInfo) bool {
|
2022-06-21 12:35:23 -06:00
|
|
|
// Delete the host itself, ensuring it's not modified anymore
|
2020-11-23 12:51:16 -07:00
|
|
|
hm.Lock()
|
2023-03-13 11:35:14 -06:00
|
|
|
// If we have a previous or next hostinfo then we are not the last one for this vpn ip
|
|
|
|
final := (hostinfo.next == nil && hostinfo.prev == nil)
|
2021-04-14 12:50:09 -06:00
|
|
|
hm.unlockedDeleteHostInfo(hostinfo)
|
2022-06-21 12:35:23 -06:00
|
|
|
hm.Unlock()
|
|
|
|
|
|
|
|
// And tear down all the relays going through this host
|
|
|
|
for _, localIdx := range hostinfo.relayState.CopyRelayForIdxs() {
|
|
|
|
hm.RemoveRelay(localIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// And tear down the relays this deleted hostInfo was using to be reached
|
|
|
|
teardownRelayIdx := []uint32{}
|
|
|
|
for _, relayIp := range hostinfo.relayState.CopyRelayIps() {
|
|
|
|
relayHostInfo, err := hm.QueryVpnIp(relayIp)
|
|
|
|
if err != nil {
|
|
|
|
hm.l.WithError(err).WithField("relay", relayIp).Info("Missing relay host in hostmap")
|
|
|
|
} else {
|
|
|
|
if r, ok := relayHostInfo.relayState.QueryRelayForByIp(hostinfo.vpnIp); ok {
|
|
|
|
teardownRelayIdx = append(teardownRelayIdx, r.LocalIndex)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, localIdx := range teardownRelayIdx {
|
|
|
|
hm.RemoveRelay(localIdx)
|
|
|
|
}
|
2023-03-13 11:35:14 -06:00
|
|
|
|
|
|
|
return final
|
2022-06-21 12:35:23 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (hm *HostMap) DeleteRelayIdx(localIdx uint32) {
|
|
|
|
hm.Lock()
|
|
|
|
defer hm.Unlock()
|
|
|
|
delete(hm.RemoteIndexes, localIdx)
|
2021-04-14 12:50:09 -06:00
|
|
|
}
|
2021-03-01 10:40:46 -07:00
|
|
|
|
2023-03-13 11:35:14 -06:00
|
|
|
func (hm *HostMap) MakePrimary(hostinfo *HostInfo) {
|
|
|
|
hm.Lock()
|
|
|
|
defer hm.Unlock()
|
|
|
|
hm.unlockedMakePrimary(hostinfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hm *HostMap) unlockedMakePrimary(hostinfo *HostInfo) {
|
|
|
|
oldHostinfo := hm.Hosts[hostinfo.vpnIp]
|
|
|
|
if oldHostinfo == hostinfo {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if hostinfo.prev != nil {
|
|
|
|
hostinfo.prev.next = hostinfo.next
|
|
|
|
}
|
|
|
|
|
|
|
|
if hostinfo.next != nil {
|
|
|
|
hostinfo.next.prev = hostinfo.prev
|
|
|
|
}
|
|
|
|
|
|
|
|
hm.Hosts[hostinfo.vpnIp] = hostinfo
|
|
|
|
|
|
|
|
if oldHostinfo == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hostinfo.next = oldHostinfo
|
|
|
|
oldHostinfo.prev = hostinfo
|
|
|
|
hostinfo.prev = nil
|
|
|
|
}
|
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
func (hm *HostMap) unlockedDeleteHostInfo(hostinfo *HostInfo) {
|
2023-03-13 11:35:14 -06:00
|
|
|
primary, ok := hm.Hosts[hostinfo.vpnIp]
|
|
|
|
if ok && primary == hostinfo {
|
|
|
|
// The vpnIp pointer points to the same hostinfo as the local index id, we can remove it
|
|
|
|
delete(hm.Hosts, hostinfo.vpnIp)
|
|
|
|
if len(hm.Hosts) == 0 {
|
|
|
|
hm.Hosts = map[iputil.VpnIp]*HostInfo{}
|
|
|
|
}
|
|
|
|
|
|
|
|
if hostinfo.next != nil {
|
|
|
|
// We had more than 1 hostinfo at this vpnip, promote the next in the list to primary
|
|
|
|
hm.Hosts[hostinfo.vpnIp] = hostinfo.next
|
|
|
|
// It is primary, there is no previous hostinfo now
|
|
|
|
hostinfo.next.prev = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Relink if we were in the middle of multiple hostinfos for this vpn ip
|
|
|
|
if hostinfo.prev != nil {
|
|
|
|
hostinfo.prev.next = hostinfo.next
|
|
|
|
}
|
|
|
|
|
|
|
|
if hostinfo.next != nil {
|
|
|
|
hostinfo.next.prev = hostinfo.prev
|
|
|
|
}
|
2021-03-01 10:40:46 -07:00
|
|
|
}
|
|
|
|
|
2023-03-13 11:35:14 -06:00
|
|
|
hostinfo.next = nil
|
|
|
|
hostinfo.prev = nil
|
|
|
|
|
|
|
|
// The remote index uses index ids outside our control so lets make sure we are only removing
|
|
|
|
// the remote index pointer here if it points to the hostinfo we are deleting
|
|
|
|
hostinfo2, ok := hm.RemoteIndexes[hostinfo.remoteIndexId]
|
|
|
|
if ok && hostinfo2 == hostinfo {
|
|
|
|
delete(hm.RemoteIndexes, hostinfo.remoteIndexId)
|
|
|
|
if len(hm.RemoteIndexes) == 0 {
|
|
|
|
hm.RemoteIndexes = map[uint32]*HostInfo{}
|
|
|
|
}
|
2020-11-23 12:51:16 -07:00
|
|
|
}
|
2023-03-13 11:35:14 -06:00
|
|
|
|
2020-11-23 12:51:16 -07:00
|
|
|
delete(hm.Indexes, hostinfo.localIndexId)
|
|
|
|
if len(hm.Indexes) == 0 {
|
|
|
|
hm.Indexes = map[uint32]*HostInfo{}
|
|
|
|
}
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level >= logrus.DebugLevel {
|
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "mapTotalSize": len(hm.Hosts),
|
2021-11-03 19:54:04 -06:00
|
|
|
"vpnIp": hostinfo.vpnIp, "indexNumber": hostinfo.localIndexId, "remoteIndexNumber": hostinfo.remoteIndexId}).
|
2020-11-23 12:51:16 -07:00
|
|
|
Debug("Hostmap hostInfo deleted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
func (hm *HostMap) QueryIndex(index uint32) (*HostInfo, error) {
|
2022-06-21 12:35:23 -06:00
|
|
|
//TODO: we probably just want to return bool instead of error, or at least a static error
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RLock()
|
|
|
|
if h, ok := hm.Indexes[index]; ok {
|
|
|
|
hm.RUnlock()
|
|
|
|
return h, nil
|
|
|
|
} else {
|
|
|
|
hm.RUnlock()
|
|
|
|
return nil, errors.New("unable to find index")
|
|
|
|
}
|
|
|
|
}
|
2022-06-21 12:35:23 -06:00
|
|
|
func (hm *HostMap) QueryRelayIndex(index uint32) (*HostInfo, error) {
|
|
|
|
//TODO: we probably just want to return bool instead of error, or at least a static error
|
|
|
|
hm.RLock()
|
|
|
|
if h, ok := hm.Relays[index]; ok {
|
|
|
|
hm.RUnlock()
|
|
|
|
return h, nil
|
|
|
|
} else {
|
|
|
|
hm.RUnlock()
|
|
|
|
return nil, errors.New("unable to find index")
|
|
|
|
}
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
|
|
|
|
func (hm *HostMap) QueryReverseIndex(index uint32) (*HostInfo, error) {
|
|
|
|
hm.RLock()
|
2020-11-23 12:51:16 -07:00
|
|
|
if h, ok := hm.RemoteIndexes[index]; ok {
|
|
|
|
hm.RUnlock()
|
|
|
|
return h, nil
|
|
|
|
} else {
|
|
|
|
hm.RUnlock()
|
|
|
|
return nil, fmt.Errorf("unable to find reverse index or connectionstate nil in %s hostmap", hm.name)
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) QueryVpnIp(vpnIp iputil.VpnIp) (*HostInfo, error) {
|
|
|
|
return hm.queryVpnIp(vpnIp, nil)
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
// PromoteBestQueryVpnIp will attempt to lazily switch to the best remote every
|
2019-11-19 10:00:20 -07:00
|
|
|
// `PromoteEvery` calls to this function for a given host.
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) PromoteBestQueryVpnIp(vpnIp iputil.VpnIp, ifce *Interface) (*HostInfo, error) {
|
|
|
|
return hm.queryVpnIp(vpnIp, ifce)
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) queryVpnIp(vpnIp iputil.VpnIp, promoteIfce *Interface) (*HostInfo, error) {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RLock()
|
|
|
|
if h, ok := hm.Hosts[vpnIp]; ok {
|
2021-05-05 12:10:55 -06:00
|
|
|
hm.RUnlock()
|
2021-04-14 12:50:09 -06:00
|
|
|
// Do not attempt promotion if you are a lighthouse
|
|
|
|
if promoteIfce != nil && !promoteIfce.lightHouse.amLighthouse {
|
2019-11-19 10:00:20 -07:00
|
|
|
h.TryPromoteBest(hm.preferredRanges, promoteIfce)
|
|
|
|
}
|
|
|
|
return h, nil
|
2021-04-14 12:50:09 -06:00
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
2021-05-05 12:10:55 -06:00
|
|
|
|
|
|
|
hm.RUnlock()
|
|
|
|
return nil, errors.New("unable to find host")
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2023-03-13 11:35:14 -06:00
|
|
|
// unlockedAddHostInfo assumes you have a write-lock and will add a hostinfo object to the hostmap Indexes and RemoteIndexes maps.
|
|
|
|
// If an entry exists for the Hosts table (vpnIp -> hostinfo) then the provided hostinfo will be made primary
|
|
|
|
func (hm *HostMap) unlockedAddHostInfo(hostinfo *HostInfo, f *Interface) {
|
2021-03-12 12:16:25 -07:00
|
|
|
if f.serveDns {
|
2021-04-14 12:50:09 -06:00
|
|
|
remoteCert := hostinfo.ConnectionState.peerCert
|
2021-03-12 12:16:25 -07:00
|
|
|
dnsR.Add(remoteCert.Details.Name+".", remoteCert.Details.Ips[0].IP.String())
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2023-03-13 11:35:14 -06:00
|
|
|
existing := hm.Hosts[hostinfo.vpnIp]
|
2021-11-03 19:54:04 -06:00
|
|
|
hm.Hosts[hostinfo.vpnIp] = hostinfo
|
2023-03-13 11:35:14 -06:00
|
|
|
|
|
|
|
if existing != nil {
|
|
|
|
hostinfo.next = existing
|
|
|
|
existing.prev = hostinfo
|
|
|
|
}
|
|
|
|
|
2021-03-12 12:16:25 -07:00
|
|
|
hm.Indexes[hostinfo.localIndexId] = hostinfo
|
|
|
|
hm.RemoteIndexes[hostinfo.remoteIndexId] = hostinfo
|
2019-11-19 10:00:20 -07:00
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
if hm.l.Level >= logrus.DebugLevel {
|
2021-11-03 19:54:04 -06:00
|
|
|
hm.l.WithField("hostMap", m{"mapName": hm.name, "vpnIp": hostinfo.vpnIp, "mapTotalSize": len(hm.Hosts),
|
|
|
|
"hostinfo": m{"existing": true, "localIndexId": hostinfo.localIndexId, "hostId": hostinfo.vpnIp}}).
|
2021-03-12 12:16:25 -07:00
|
|
|
Debug("Hostmap vpnIp added")
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
2023-03-13 11:35:14 -06:00
|
|
|
|
|
|
|
i := 1
|
|
|
|
check := hostinfo
|
|
|
|
for check != nil {
|
|
|
|
if i > MaxHostInfosPerVpnIp {
|
|
|
|
hm.unlockedDeleteHostInfo(check)
|
|
|
|
}
|
|
|
|
check = check.next
|
|
|
|
i++
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
// punchList assembles a list of all non nil RemoteList pointer entries in this hostmap
|
|
|
|
// The caller can then do the its work outside of the read lock
|
|
|
|
func (hm *HostMap) punchList(rl []*RemoteList) []*RemoteList {
|
2019-11-19 10:00:20 -07:00
|
|
|
hm.RLock()
|
2021-04-14 12:50:09 -06:00
|
|
|
defer hm.RUnlock()
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
for _, v := range hm.Hosts {
|
2021-04-14 12:50:09 -06:00
|
|
|
if v.remotes != nil {
|
|
|
|
rl = append(rl, v.remotes)
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
}
|
2021-04-14 12:50:09 -06:00
|
|
|
return rl
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
// Punchy iterates through the result of punchList() to assemble all known addresses and sends a hole punch packet to them
|
2021-11-03 19:54:04 -06:00
|
|
|
func (hm *HostMap) Punchy(ctx context.Context, conn *udp.Conn) {
|
2020-06-26 11:45:48 -06:00
|
|
|
var metricsTxPunchy metrics.Counter
|
|
|
|
if hm.metricsEnabled {
|
|
|
|
metricsTxPunchy = metrics.GetOrRegisterCounter("messages.tx.punchy", nil)
|
|
|
|
} else {
|
|
|
|
metricsTxPunchy = metrics.NilCounter{}
|
|
|
|
}
|
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
var remotes []*RemoteList
|
2021-03-18 19:37:24 -06:00
|
|
|
b := []byte{1}
|
2021-11-02 12:14:26 -06:00
|
|
|
|
|
|
|
clockSource := time.NewTicker(time.Second * 10)
|
|
|
|
defer clockSource.Stop()
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
for {
|
2021-04-14 12:50:09 -06:00
|
|
|
remotes = hm.punchList(remotes[:0])
|
|
|
|
for _, rl := range remotes {
|
|
|
|
//TODO: CopyAddrs generates garbage but ForEach locks for the work here, figure out which way is better
|
|
|
|
for _, addr := range rl.CopyAddrs(hm.preferredRanges) {
|
|
|
|
metricsTxPunchy.Inc(1)
|
|
|
|
conn.WriteTo(b, addr)
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
2021-11-02 12:14:26 -06:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
case <-clockSource.C:
|
|
|
|
continue
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
// TryPromoteBest handles re-querying lighthouses and probing for better paths
|
|
|
|
// NOTE: It is an error to call this if you are a lighthouse since they should not roam clients!
|
2019-11-19 10:00:20 -07:00
|
|
|
func (i *HostInfo) TryPromoteBest(preferredRanges []*net.IPNet, ifce *Interface) {
|
2022-10-31 11:37:41 -06:00
|
|
|
c := i.promoteCounter.Add(1)
|
2021-04-14 12:50:09 -06:00
|
|
|
if c%PromoteEvery == 0 {
|
2021-05-05 12:10:55 -06:00
|
|
|
// The lock here is currently protecting i.remote access
|
|
|
|
i.RLock()
|
2022-06-21 12:35:23 -06:00
|
|
|
remote := i.remote
|
|
|
|
i.RUnlock()
|
2021-05-05 12:10:55 -06:00
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
// return early if we are already on a preferred remote
|
2022-06-21 12:35:23 -06:00
|
|
|
if remote != nil {
|
|
|
|
rIP := remote.IP
|
|
|
|
for _, l := range preferredRanges {
|
|
|
|
if l.Contains(rIP) {
|
|
|
|
return
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
i.remotes.ForEach(preferredRanges, func(addr *udp.Addr, preferred bool) {
|
2022-06-21 12:35:23 -06:00
|
|
|
if remote != nil && (addr == nil || !preferred) {
|
2021-04-14 12:50:09 -06:00
|
|
|
return
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
|
|
|
|
// Try to send a test packet to that host, this should
|
|
|
|
// cause it to detect a roaming event and switch remotes
|
2022-06-21 12:35:23 -06:00
|
|
|
ifce.sendTo(header.Test, header.TestRequest, i.ConnectionState, i, addr, []byte(""), make([]byte, 12, 12), make([]byte, mtu))
|
2021-04-14 12:50:09 -06:00
|
|
|
})
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
// Re query our lighthouses for new remotes occasionally
|
|
|
|
if c%ReQueryEvery == 0 && ifce.lightHouse != nil {
|
2021-11-03 19:54:04 -06:00
|
|
|
ifce.lightHouse.QueryServer(i.vpnIp, ifce)
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (i *HostInfo) cachePacket(l *logrus.Logger, t header.MessageType, st header.MessageSubType, packet []byte, f packetCallback, m *cachedPacketMetrics) {
|
2019-11-19 10:00:20 -07:00
|
|
|
//TODO: return the error so we can log with more context
|
|
|
|
if len(i.packetStore) < 100 {
|
|
|
|
tempPacket := make([]byte, len(packet))
|
|
|
|
copy(tempPacket, packet)
|
|
|
|
//l.WithField("trace", string(debug.Stack())).Error("Caching packet", tempPacket)
|
|
|
|
i.packetStore = append(i.packetStore, &cachedPacket{t, st, f, tempPacket})
|
2021-03-05 19:18:33 -07:00
|
|
|
if l.Level >= logrus.DebugLevel {
|
2021-03-26 08:46:30 -06:00
|
|
|
i.logger(l).
|
2021-03-05 19:18:33 -07:00
|
|
|
WithField("length", len(i.packetStore)).
|
|
|
|
WithField("stored", true).
|
|
|
|
Debugf("Packet store")
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
|
|
|
|
} else if l.Level >= logrus.DebugLevel {
|
2021-04-27 20:23:18 -06:00
|
|
|
m.dropped.Inc(1)
|
2021-03-26 08:46:30 -06:00
|
|
|
i.logger(l).
|
2019-11-19 10:00:20 -07:00
|
|
|
WithField("length", len(i.packetStore)).
|
|
|
|
WithField("stored", false).
|
|
|
|
Debugf("Packet store")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handshakeComplete will set the connection as ready to communicate, as well as flush any stored packets
|
2021-04-27 20:23:18 -06:00
|
|
|
func (i *HostInfo) handshakeComplete(l *logrus.Logger, m *cachedPacketMetrics) {
|
2019-11-19 10:00:20 -07:00
|
|
|
//TODO: I'm not certain the distinction between handshake complete and ConnectionState being ready matters because:
|
|
|
|
//TODO: HandshakeComplete means send stored packets and ConnectionState.ready means we are ready to send
|
|
|
|
//TODO: if the transition from HandhsakeComplete to ConnectionState.ready happens all within this function they are identical
|
|
|
|
|
|
|
|
i.ConnectionState.queueLock.Lock()
|
|
|
|
i.HandshakeComplete = true
|
|
|
|
//TODO: this should be managed by the handshake state machine to set it based on how many handshake were seen.
|
|
|
|
// Clamping it to 2 gets us out of the woods for now
|
2022-10-31 11:37:41 -06:00
|
|
|
i.ConnectionState.messageCounter.Store(2)
|
2021-03-22 17:25:06 -06:00
|
|
|
|
|
|
|
if l.Level >= logrus.DebugLevel {
|
2021-03-26 08:46:30 -06:00
|
|
|
i.logger(l).Debugf("Sending %d stored packets", len(i.packetStore))
|
2021-03-22 17:25:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(i.packetStore) > 0 {
|
|
|
|
nb := make([]byte, 12, 12)
|
|
|
|
out := make([]byte, mtu)
|
|
|
|
for _, cp := range i.packetStore {
|
|
|
|
cp.callback(cp.messageType, cp.messageSubType, i, cp.packet, nb, out)
|
|
|
|
}
|
2021-04-27 20:23:18 -06:00
|
|
|
m.sent.Inc(int64(len(i.packetStore)))
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
2021-03-22 17:25:06 -06:00
|
|
|
|
2021-04-14 12:50:09 -06:00
|
|
|
i.remotes.ResetBlockedRemotes()
|
2019-11-19 10:00:20 -07:00
|
|
|
i.packetStore = make([]*cachedPacket, 0)
|
|
|
|
i.ConnectionState.ready = true
|
|
|
|
i.ConnectionState.queueLock.Unlock()
|
|
|
|
i.ConnectionState.certState = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *HostInfo) GetCert() *cert.NebulaCertificate {
|
|
|
|
if i.ConnectionState != nil {
|
|
|
|
return i.ConnectionState.peerCert
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
func (i *HostInfo) SetRemote(remote *udp.Addr) {
|
2021-04-14 12:50:09 -06:00
|
|
|
// We copy here because we likely got this remote from a source that reuses the object
|
|
|
|
if !i.remote.Equals(remote) {
|
|
|
|
i.remote = remote.Copy()
|
2021-11-03 19:54:04 -06:00
|
|
|
i.remotes.LearnRemote(i.vpnIp, remote.Copy())
|
2021-03-31 09:26:35 -06:00
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
}
|
|
|
|
|
2021-10-19 08:53:55 -06:00
|
|
|
// SetRemoteIfPreferred returns true if the remote was changed. The lastRoam
|
|
|
|
// time on the HostInfo will also be updated.
|
2021-11-03 19:54:04 -06:00
|
|
|
func (i *HostInfo) SetRemoteIfPreferred(hm *HostMap, newRemote *udp.Addr) bool {
|
2022-06-21 12:35:23 -06:00
|
|
|
if newRemote == nil {
|
|
|
|
// relays have nil udp Addrs
|
|
|
|
return false
|
|
|
|
}
|
2021-10-19 08:53:55 -06:00
|
|
|
currentRemote := i.remote
|
|
|
|
if currentRemote == nil {
|
|
|
|
i.SetRemote(newRemote)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: We do this loop here instead of calling `isPreferred` in
|
|
|
|
// remote_list.go so that we only have to loop over preferredRanges once.
|
|
|
|
newIsPreferred := false
|
|
|
|
for _, l := range hm.preferredRanges {
|
|
|
|
// return early if we are already on a preferred remote
|
|
|
|
if l.Contains(currentRemote.IP) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if l.Contains(newRemote.IP) {
|
|
|
|
newIsPreferred = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if newIsPreferred {
|
|
|
|
// Consider this a roaming event
|
|
|
|
i.lastRoam = time.Now()
|
|
|
|
i.lastRoamRemote = currentRemote.Copy()
|
|
|
|
|
|
|
|
i.SetRemote(newRemote)
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
func (i *HostInfo) RecvErrorExceeded() bool {
|
|
|
|
if i.recvError < 3 {
|
|
|
|
i.recvError += 1
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-12-12 09:34:17 -07:00
|
|
|
func (i *HostInfo) CreateRemoteCIDR(c *cert.NebulaCertificate) {
|
2020-03-02 14:21:33 -07:00
|
|
|
if len(c.Details.Ips) == 1 && len(c.Details.Subnets) == 0 {
|
|
|
|
// Simple case, no CIDRTree needed
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-03 19:54:04 -06:00
|
|
|
remoteCidr := cidr.NewTree4()
|
2019-12-12 09:34:17 -07:00
|
|
|
for _, ip := range c.Details.Ips {
|
|
|
|
remoteCidr.AddCIDR(&net.IPNet{IP: ip.IP, Mask: net.IPMask{255, 255, 255, 255}}, struct{}{})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, n := range c.Details.Subnets {
|
|
|
|
remoteCidr.AddCIDR(n, struct{}{})
|
|
|
|
}
|
|
|
|
i.remoteCidr = remoteCidr
|
|
|
|
}
|
|
|
|
|
2021-03-26 08:46:30 -06:00
|
|
|
func (i *HostInfo) logger(l *logrus.Logger) *logrus.Entry {
|
2020-04-06 12:34:00 -06:00
|
|
|
if i == nil {
|
|
|
|
return logrus.NewEntry(l)
|
|
|
|
}
|
|
|
|
|
2023-02-13 13:41:05 -07:00
|
|
|
li := l.WithField("vpnIp", i.vpnIp).
|
|
|
|
WithField("localIndex", i.localIndexId).
|
|
|
|
WithField("remoteIndex", i.remoteIndexId)
|
|
|
|
|
2020-04-06 12:34:00 -06:00
|
|
|
if connState := i.ConnectionState; connState != nil {
|
|
|
|
if peerCert := connState.peerCert; peerCert != nil {
|
|
|
|
li = li.WithField("certName", peerCert.Details.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return li
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
// Utility functions
|
|
|
|
|
2021-10-19 08:54:30 -06:00
|
|
|
func localIps(l *logrus.Logger, allowList *LocalAllowList) *[]net.IP {
|
2019-11-19 10:00:20 -07:00
|
|
|
//FIXME: This function is pretty garbage
|
|
|
|
var ips []net.IP
|
|
|
|
ifaces, _ := net.Interfaces()
|
|
|
|
for _, i := range ifaces {
|
Add lighthouse.{remoteAllowList,localAllowList} (#217)
These settings make it possible to blacklist / whitelist IP addresses
that are used for remote connections.
`lighthouse.remoteAllowList` filters which remote IPs are allow when
fetching from the lighthouse (or, if you are the lighthouse, which IPs
you store and forward to querying hosts). By default, any remote IPs are
allowed. You can provide CIDRs here with `true` to allow and `false` to
deny. The most specific CIDR rule applies to each remote. If all rules
are "allow", the default will be "deny", and vice-versa. If both "allow"
and "deny" rules are present, then you MUST set a rule for "0.0.0.0/0"
as the default.
lighthouse:
remoteAllowList:
# Example to block IPs from this subnet from being used for remote IPs.
"172.16.0.0/12": false
# A more complicated example, allow public IPs but only private IPs from a specific subnet
"0.0.0.0/0": true
"10.0.0.0/8": false
"10.42.42.0/24": true
`lighthouse.localAllowList` has the same logic as above, but it applies
to the local addresses we advertise to the lighthouse. Additionally, you
can specify an `interfaces` map of regular expressions to match against
interface names. The regexp must match the entire name. All interface
rules must be either true or false (and the default rule will be the
inverse). CIDR rules are matched after interface name rules.
Default is all local IP addresses.
lighthouse:
localAllowList:
# Example to blacklist docker interfaces.
interfaces:
'docker.*': false
# Example to only advertise IPs in this subnet to the lighthouse.
"10.0.0.0/8": true
2020-04-08 13:36:43 -06:00
|
|
|
allow := allowList.AllowName(i.Name)
|
2021-03-31 09:26:35 -06:00
|
|
|
if l.Level >= logrus.TraceLevel {
|
|
|
|
l.WithField("interfaceName", i.Name).WithField("allow", allow).Trace("localAllowList.AllowName")
|
|
|
|
}
|
|
|
|
|
Add lighthouse.{remoteAllowList,localAllowList} (#217)
These settings make it possible to blacklist / whitelist IP addresses
that are used for remote connections.
`lighthouse.remoteAllowList` filters which remote IPs are allow when
fetching from the lighthouse (or, if you are the lighthouse, which IPs
you store and forward to querying hosts). By default, any remote IPs are
allowed. You can provide CIDRs here with `true` to allow and `false` to
deny. The most specific CIDR rule applies to each remote. If all rules
are "allow", the default will be "deny", and vice-versa. If both "allow"
and "deny" rules are present, then you MUST set a rule for "0.0.0.0/0"
as the default.
lighthouse:
remoteAllowList:
# Example to block IPs from this subnet from being used for remote IPs.
"172.16.0.0/12": false
# A more complicated example, allow public IPs but only private IPs from a specific subnet
"0.0.0.0/0": true
"10.0.0.0/8": false
"10.42.42.0/24": true
`lighthouse.localAllowList` has the same logic as above, but it applies
to the local addresses we advertise to the lighthouse. Additionally, you
can specify an `interfaces` map of regular expressions to match against
interface names. The regexp must match the entire name. All interface
rules must be either true or false (and the default rule will be the
inverse). CIDR rules are matched after interface name rules.
Default is all local IP addresses.
lighthouse:
localAllowList:
# Example to blacklist docker interfaces.
interfaces:
'docker.*': false
# Example to only advertise IPs in this subnet to the lighthouse.
"10.0.0.0/8": true
2020-04-08 13:36:43 -06:00
|
|
|
if !allow {
|
|
|
|
continue
|
|
|
|
}
|
2019-11-19 10:00:20 -07:00
|
|
|
addrs, _ := i.Addrs()
|
|
|
|
for _, addr := range addrs {
|
|
|
|
var ip net.IP
|
|
|
|
switch v := addr.(type) {
|
|
|
|
case *net.IPNet:
|
|
|
|
//continue
|
|
|
|
ip = v.IP
|
|
|
|
case *net.IPAddr:
|
|
|
|
ip = v.IP
|
|
|
|
}
|
2021-03-18 19:37:24 -06:00
|
|
|
|
|
|
|
//TODO: Filtering out link local for now, this is probably the most correct thing
|
|
|
|
//TODO: Would be nice to filter out SLAAC MAC based ips as well
|
|
|
|
if ip.IsLoopback() == false && !ip.IsLinkLocalUnicast() {
|
|
|
|
allow := allowList.Allow(ip)
|
2021-03-31 09:26:35 -06:00
|
|
|
if l.Level >= logrus.TraceLevel {
|
|
|
|
l.WithField("localIp", ip).WithField("allow", allow).Trace("localAllowList.Allow")
|
|
|
|
}
|
Add lighthouse.{remoteAllowList,localAllowList} (#217)
These settings make it possible to blacklist / whitelist IP addresses
that are used for remote connections.
`lighthouse.remoteAllowList` filters which remote IPs are allow when
fetching from the lighthouse (or, if you are the lighthouse, which IPs
you store and forward to querying hosts). By default, any remote IPs are
allowed. You can provide CIDRs here with `true` to allow and `false` to
deny. The most specific CIDR rule applies to each remote. If all rules
are "allow", the default will be "deny", and vice-versa. If both "allow"
and "deny" rules are present, then you MUST set a rule for "0.0.0.0/0"
as the default.
lighthouse:
remoteAllowList:
# Example to block IPs from this subnet from being used for remote IPs.
"172.16.0.0/12": false
# A more complicated example, allow public IPs but only private IPs from a specific subnet
"0.0.0.0/0": true
"10.0.0.0/8": false
"10.42.42.0/24": true
`lighthouse.localAllowList` has the same logic as above, but it applies
to the local addresses we advertise to the lighthouse. Additionally, you
can specify an `interfaces` map of regular expressions to match against
interface names. The regexp must match the entire name. All interface
rules must be either true or false (and the default rule will be the
inverse). CIDR rules are matched after interface name rules.
Default is all local IP addresses.
lighthouse:
localAllowList:
# Example to blacklist docker interfaces.
interfaces:
'docker.*': false
# Example to only advertise IPs in this subnet to the lighthouse.
"10.0.0.0/8": true
2020-04-08 13:36:43 -06:00
|
|
|
if !allow {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-11-19 10:00:20 -07:00
|
|
|
ips = append(ips, ip)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &ips
|
|
|
|
}
|