2019-11-19 17:00:20 +00:00
package nebula
import (
2025-12-03 18:48:29 -05:00
"encoding/json"
2019-11-19 17:00:20 +00:00
"errors"
2025-12-03 18:48:29 -05:00
"fmt"
2019-11-19 17:00:20 +00:00
"net"
2024-07-31 10:18:56 -05:00
"net/netip"
2025-07-02 08:47:05 -04:00
"slices"
2019-11-19 17:00:20 +00:00
"sync"
2021-03-05 21:18:33 -05:00
"sync/atomic"
2019-11-19 17:00:20 +00:00
"time"
2024-07-31 10:18:56 -05:00
"github.com/gaissmai/bart"
2019-11-19 17:00:20 +00:00
"github.com/rcrowley/go-metrics"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/cert"
2024-04-03 22:14:51 -05:00
"github.com/slackhq/nebula/config"
2021-11-03 20:54:04 -05:00
"github.com/slackhq/nebula/header"
2019-11-19 17:00:20 +00:00
)
2023-08-08 13:26:41 -05:00
const defaultPromoteEvery = 1000 // Count of packets sent before we try moving a tunnel to a preferred underlay ip address
const defaultReQueryEvery = 5000 // Count of packets sent before re-querying a hostinfo to the lighthouse
const defaultReQueryWait = time . Minute // Minimum amount of seconds to wait before re-querying a hostinfo the lighthouse. Evaluated every ReQueryEvery
2019-11-19 17:00:20 +00:00
const MaxRemotes = 10
2023-03-13 12:35:14 -05: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 17:00:20 +00: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 17:14:34 +01:00
const RoamingSuppressSeconds = 2
2019-11-19 17:00:20 +00:00
2022-06-21 14:35:23 -04:00
const (
Requested = iota
2023-05-04 15:16:37 -05:00
PeerRequested
2022-06-21 14:35:23 -04:00
Established
2025-03-06 11:28:26 -06:00
Disestablished
2022-06-21 14:35:23 -04:00
)
const (
Unknowntype = iota
ForwardingType
TerminalType
)
type Relay struct {
Type int
State int
LocalIndex uint32
RemoteIndex uint32
2025-03-06 11:28:26 -06:00
PeerAddr netip . Addr
2022-06-21 14:35:23 -04:00
}
2019-11-19 17:00:20 +00:00
type HostMap struct {
sync . RWMutex //Because we concurrently read and write to our maps
Indexes map [ uint32 ] * HostInfo
2022-06-21 14:35:23 -04:00
Relays map [ uint32 ] * HostInfo // Maps a Relay IDX to a Relay HostInfo object
2020-11-23 14:51:16 -05:00
RemoteIndexes map [ uint32 ] * HostInfo
2024-07-31 10:18:56 -05:00
Hosts map [ netip . Addr ] * HostInfo
preferredRanges atomic . Pointer [ [ ] netip . Prefix ]
2021-03-26 09:46:30 -05:00
l * logrus . Logger
2019-11-19 17:00:20 +00:00
}
2023-03-30 12:09:20 -04:00
// For synchronization, treat the pointed-to Relay struct as immutable. To edit the Relay
// struct, make a copy of an existing value, edit the fileds in the copy, and
// then store a pointer to the new copy in both realyForBy* maps.
2022-06-21 14:35:23 -04:00
type RelayState struct {
sync . RWMutex
2025-07-02 08:47:05 -04:00
relays [ ] netip . Addr // Ordered set of VpnAddrs of Hosts to use as relays to access this peer
2025-03-06 11:28:26 -06:00
// For data race avoidance, the contents of a *Relay are treated immutably. To update a *Relay, copy the existing data,
// modify what needs to be updated, and store the new modified copy in the relayForByIp and relayForByIdx maps (with
// the RelayState Lock held)
relayForByAddr map [ netip . Addr ] * Relay // Maps vpnAddr 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
2022-06-21 14:35:23 -04:00
}
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) DeleteRelay ( ip netip . Addr ) {
2022-06-21 14:35:23 -04:00
rs . Lock ( )
defer rs . Unlock ( )
2025-07-02 08:47:05 -04:00
for idx , val := range rs . relays {
if val == ip {
rs . relays = append ( rs . relays [ : idx ] , rs . relays [ idx + 1 : ] ... )
return
}
}
2022-06-21 14:35:23 -04:00
}
2025-03-06 11:28:26 -06:00
func ( rs * RelayState ) UpdateRelayForByIpState ( vpnIp netip . Addr , state int ) {
rs . Lock ( )
defer rs . Unlock ( )
if r , ok := rs . relayForByAddr [ vpnIp ] ; ok {
newRelay := * r
newRelay . State = state
rs . relayForByAddr [ newRelay . PeerAddr ] = & newRelay
rs . relayForByIdx [ newRelay . LocalIndex ] = & newRelay
}
}
func ( rs * RelayState ) UpdateRelayForByIdxState ( idx uint32 , state int ) {
rs . Lock ( )
defer rs . Unlock ( )
if r , ok := rs . relayForByIdx [ idx ] ; ok {
newRelay := * r
newRelay . State = state
rs . relayForByAddr [ newRelay . PeerAddr ] = & newRelay
rs . relayForByIdx [ newRelay . LocalIndex ] = & newRelay
}
}
2023-05-04 15:16:37 -05:00
func ( rs * RelayState ) CopyAllRelayFor ( ) [ ] * Relay {
rs . RLock ( )
defer rs . RUnlock ( )
ret := make ( [ ] * Relay , 0 , len ( rs . relayForByIdx ) )
for _ , r := range rs . relayForByIdx {
ret = append ( ret , r )
}
return ret
}
2025-03-06 11:28:26 -06:00
func ( rs * RelayState ) GetRelayForByAddr ( addr netip . Addr ) ( * Relay , bool ) {
2022-06-21 14:35:23 -04:00
rs . RLock ( )
defer rs . RUnlock ( )
2025-03-06 11:28:26 -06:00
r , ok := rs . relayForByAddr [ addr ]
2022-06-21 14:35:23 -04:00
return r , ok
}
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) InsertRelayTo ( ip netip . Addr ) {
2022-06-21 14:35:23 -04:00
rs . Lock ( )
defer rs . Unlock ( )
2025-07-02 08:47:05 -04:00
if ! slices . Contains ( rs . relays , ip ) {
rs . relays = append ( rs . relays , ip )
}
2022-06-21 14:35:23 -04:00
}
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) CopyRelayIps ( ) [ ] netip . Addr {
2025-07-02 08:47:05 -04:00
ret := make ( [ ] netip . Addr , len ( rs . relays ) )
2022-06-21 14:35:23 -04:00
rs . RLock ( )
defer rs . RUnlock ( )
2025-07-02 08:47:05 -04:00
copy ( ret , rs . relays )
2022-06-21 14:35:23 -04:00
return ret
}
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) CopyRelayForIps ( ) [ ] netip . Addr {
2022-06-21 14:35:23 -04:00
rs . RLock ( )
defer rs . RUnlock ( )
2025-03-06 11:28:26 -06:00
currentRelays := make ( [ ] netip . Addr , 0 , len ( rs . relayForByAddr ) )
for relayIp := range rs . relayForByAddr {
2022-06-21 14:35:23 -04:00
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
}
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) CompleteRelayByIP ( vpnIp netip . Addr , remoteIdx uint32 ) bool {
2023-03-30 12:09:20 -04:00
rs . Lock ( )
defer rs . Unlock ( )
2025-03-06 11:28:26 -06:00
r , ok := rs . relayForByAddr [ vpnIp ]
2023-03-30 12:09:20 -04:00
if ! ok {
return false
}
newRelay := * r
newRelay . State = Established
newRelay . RemoteIndex = remoteIdx
rs . relayForByIdx [ r . LocalIndex ] = & newRelay
2025-03-06 11:28:26 -06:00
rs . relayForByAddr [ r . PeerAddr ] = & newRelay
2023-03-30 12:09:20 -04:00
return true
}
func ( rs * RelayState ) CompleteRelayByIdx ( localIdx uint32 , remoteIdx uint32 ) ( * Relay , bool ) {
rs . Lock ( )
defer rs . Unlock ( )
r , ok := rs . relayForByIdx [ localIdx ]
if ! ok {
return nil , false
}
newRelay := * r
newRelay . State = Established
newRelay . RemoteIndex = remoteIdx
rs . relayForByIdx [ r . LocalIndex ] = & newRelay
2025-03-06 11:28:26 -06:00
rs . relayForByAddr [ r . PeerAddr ] = & newRelay
2023-03-30 12:09:20 -04:00
return & newRelay , true
2022-06-21 14:35:23 -04:00
}
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) QueryRelayForByIp ( vpnIp netip . Addr ) ( * Relay , bool ) {
2022-06-21 14:35:23 -04:00
rs . RLock ( )
defer rs . RUnlock ( )
2025-03-06 11:28:26 -06:00
r , ok := rs . relayForByAddr [ vpnIp ]
2022-06-21 14:35:23 -04:00
return r , ok
}
func ( rs * RelayState ) QueryRelayForByIdx ( idx uint32 ) ( * Relay , bool ) {
rs . RLock ( )
defer rs . RUnlock ( )
r , ok := rs . relayForByIdx [ idx ]
return r , ok
}
2023-03-30 12:09:20 -04:00
2024-07-31 10:18:56 -05:00
func ( rs * RelayState ) InsertRelay ( ip netip . Addr , idx uint32 , r * Relay ) {
2022-06-21 14:35:23 -04:00
rs . Lock ( )
defer rs . Unlock ( )
2025-03-06 11:28:26 -06:00
rs . relayForByAddr [ ip ] = r
2022-06-21 14:35:23 -04:00
rs . relayForByIdx [ idx ] = r
}
2025-11-12 13:40:20 -06:00
type NetworkType uint8
const (
NetworkTypeUnknown NetworkType = iota
// NetworkTypeVPN is a network that overlaps one or more of the vpnNetworks in our certificate
NetworkTypeVPN
// NetworkTypeVPNPeer is a network that does not overlap one of our networks
NetworkTypeVPNPeer
// NetworkTypeUnsafe is a network from Certificate.UnsafeNetworks()
NetworkTypeUnsafe
)
2019-11-19 17:00:20 +00:00
type HostInfo struct {
2024-07-31 10:18:56 -05:00
remote netip . AddrPort
2023-11-02 16:53:59 -05:00
remotes * RemoteList
promoteCounter atomic . Uint32
ConnectionState * ConnectionState
remoteIndexId uint32
localIndexId uint32
2025-03-06 11:28:26 -06:00
// vpnAddrs is a list of vpn addresses assigned to this host that are within our own vpn networks
// The host may have other vpn addresses that are outside our
// vpn networks but were removed because they are not usable
2025-09-03 11:52:52 -05:00
vpnAddrs [ ] netip . Addr
2025-03-06 11:28:26 -06:00
2025-11-12 13:40:20 -06:00
// networks is a combination of specific vpn addresses (not prefixes!) and full unsafe networks assigned to this host.
networks * bart . Table [ NetworkType ]
2025-03-06 11:28:26 -06:00
relayState RelayState
2023-11-02 16:53:59 -05:00
// HandshakePacket records the packets used to create this hostinfo
// We need these to avoid replayed handshake packets creating new hostinfos which causes churn
HandshakePacket map [ uint8 ] [ ] byte
2019-11-19 17:00:20 +00:00
2023-08-08 13:26:41 -05:00
// nextLHQuery is the earliest we can ask the lighthouse for new information.
// This is used to limit lighthouse re-queries in chatty clients
nextLHQuery atomic . Int64
2021-03-01 19:06:01 -06: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 21:15:34 -05: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 17:00:20 +00:00
lastRoam time . Time
2024-07-31 10:18:56 -05:00
lastRoamRemote netip . AddrPort
2023-03-13 12:35:14 -05: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
2025-07-03 09:58:37 -05:00
//TODO: in, out, and others might benefit from being an atomic.Int32. We could collapse connectionManager pendingDeletion, relayUsed, and in/out into this 1 thing
in , out , pendingDeletion atomic . Bool
// lastUsed tracks the last time ConnectionManager checked the tunnel and it was in use.
// This value will be behind against actual tunnel utilization in the hot path.
// This should only be used by the ConnectionManagers ticker routine.
lastUsed time . Time
2019-11-19 17:00:20 +00:00
}
2022-06-21 14:35:23 -04:00
type ViaSender struct {
2025-12-03 18:48:29 -05:00
UdpAddr netip . AddrPort
2022-06-21 14:35:23 -04:00
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.
2025-12-03 18:48:29 -05:00
IsRelayed bool // IsRelayed is true if the packet was sent through a relay
}
func ( v ViaSender ) String ( ) string {
if v . IsRelayed {
return fmt . Sprintf ( "%s (relayed)" , v . UdpAddr )
}
return v . UdpAddr . String ( )
}
func ( v ViaSender ) MarshalJSON ( ) ( [ ] byte , error ) {
if v . IsRelayed {
return json . Marshal ( m { "relay" : v . UdpAddr } )
}
return json . Marshal ( m { "direct" : v . UdpAddr } )
2022-06-21 14:35:23 -04:00
}
2019-11-19 17:00:20 +00:00
type cachedPacket struct {
2021-11-03 20:54:04 -05:00
messageType header . MessageType
messageSubType header . MessageSubType
2019-11-19 17:00:20 +00:00
callback packetCallback
packet [ ] byte
}
2021-11-03 20:54:04 -05:00
type packetCallback func ( t header . MessageType , st header . MessageSubType , h * HostInfo , p , nb , out [ ] byte )
2019-11-19 17:00:20 +00:00
2021-04-27 22:23:18 -04:00
type cachedPacketMetrics struct {
sent metrics . Counter
dropped metrics . Counter
}
2025-03-06 11:28:26 -06:00
func NewHostMapFromConfig ( l * logrus . Logger , c * config . C ) * HostMap {
hm := newHostMap ( l )
2024-04-03 22:14:51 -05:00
hm . reload ( c , true )
c . RegisterReloadCallback ( func ( c * config . C ) {
hm . reload ( c , false )
} )
2025-03-06 11:28:26 -06:00
l . WithField ( "preferredRanges" , hm . GetPreferredRanges ( ) ) .
2024-04-03 22:14:51 -05:00
Info ( "Main HostMap created" )
return hm
}
2025-03-06 11:28:26 -06:00
func newHostMap ( l * logrus . Logger ) * HostMap {
2024-04-03 22:14:51 -05:00
return & HostMap {
Indexes : map [ uint32 ] * HostInfo { } ,
Relays : map [ uint32 ] * HostInfo { } ,
RemoteIndexes : map [ uint32 ] * HostInfo { } ,
2024-07-31 10:18:56 -05:00
Hosts : map [ netip . Addr ] * HostInfo { } ,
2024-04-03 22:14:51 -05:00
l : l ,
}
}
func ( hm * HostMap ) reload ( c * config . C , initial bool ) {
if initial || c . HasChanged ( "preferred_ranges" ) {
2024-07-31 10:18:56 -05:00
var preferredRanges [ ] netip . Prefix
2024-04-03 22:14:51 -05:00
rawPreferredRanges := c . GetStringSlice ( "preferred_ranges" , [ ] string { } )
for _ , rawPreferredRange := range rawPreferredRanges {
2024-07-31 10:18:56 -05:00
preferredRange , err := netip . ParsePrefix ( rawPreferredRange )
2024-04-03 22:14:51 -05:00
if err != nil {
hm . l . WithError ( err ) . WithField ( "range" , rawPreferredRanges ) . Warn ( "Failed to parse preferred ranges, ignoring" )
continue
}
preferredRanges = append ( preferredRanges , preferredRange )
}
oldRanges := hm . preferredRanges . Swap ( & preferredRanges )
if ! initial {
hm . l . WithField ( "oldPreferredRanges" , * oldRanges ) . WithField ( "newPreferredRanges" , preferredRanges ) . Info ( "preferred_ranges changed" )
}
2019-11-19 17:00:20 +00:00
}
}
2023-07-24 12:37:52 -05:00
// EmitStats reports host, index, and relay counts to the stats collection system
func ( hm * HostMap ) EmitStats ( ) {
2019-11-19 17:00:20 +00:00
hm . RLock ( )
hostLen := len ( hm . Hosts )
indexLen := len ( hm . Indexes )
2020-11-23 14:51:16 -05:00
remoteIndexLen := len ( hm . RemoteIndexes )
2022-06-21 14:35:23 -04:00
relaysLen := len ( hm . Relays )
2019-11-19 17:00:20 +00:00
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
metrics . GetOrRegisterGauge ( "hostmap.main.hosts" , nil ) . Update ( int64 ( hostLen ) )
metrics . GetOrRegisterGauge ( "hostmap.main.indexes" , nil ) . Update ( int64 ( indexLen ) )
metrics . GetOrRegisterGauge ( "hostmap.main.remoteIndexes" , nil ) . Update ( int64 ( remoteIndexLen ) )
metrics . GetOrRegisterGauge ( "hostmap.main.relayIndexes" , nil ) . Update ( int64 ( relaysLen ) )
2022-06-21 14:35:23 -04:00
}
2023-03-13 12:35:14 -05: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 14:35:23 -04:00
// Delete the host itself, ensuring it's not modified anymore
2020-11-23 14:51:16 -05:00
hm . Lock ( )
2023-03-13 12:35:14 -05: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 13:50:09 -05:00
hm . unlockedDeleteHostInfo ( hostinfo )
2022-06-21 14:35:23 -04:00
hm . Unlock ( )
2023-03-13 12:35:14 -05:00
return final
2022-06-21 14:35:23 -04:00
}
2023-03-13 12:35:14 -05:00
func ( hm * HostMap ) MakePrimary ( hostinfo * HostInfo ) {
hm . Lock ( )
defer hm . Unlock ( )
hm . unlockedMakePrimary ( hostinfo )
}
func ( hm * HostMap ) unlockedMakePrimary ( hostinfo * HostInfo ) {
2025-03-06 11:28:26 -06:00
// Get the current primary, if it exists
oldHostinfo := hm . Hosts [ hostinfo . vpnAddrs [ 0 ] ]
// Every address in the hostinfo gets elevated to primary
for _ , vpnAddr := range hostinfo . vpnAddrs {
//NOTE: It is possible that we leave a dangling hostinfo here but connection manager works on
// indexes so it should be fine.
hm . Hosts [ vpnAddr ] = hostinfo
}
// If we are already primary then we won't bother re-linking
2023-03-13 12:35:14 -05:00
if oldHostinfo == hostinfo {
return
}
2025-03-06 11:28:26 -06:00
// Unlink this hostinfo
2023-03-13 12:35:14 -05:00
if hostinfo . prev != nil {
hostinfo . prev . next = hostinfo . next
}
if hostinfo . next != nil {
hostinfo . next . prev = hostinfo . prev
}
2025-03-06 11:28:26 -06:00
// If there wasn't a previous primary then clear out any links
2023-03-13 12:35:14 -05:00
if oldHostinfo == nil {
2025-03-06 11:28:26 -06:00
hostinfo . next = nil
hostinfo . prev = nil
2023-03-13 12:35:14 -05:00
return
}
2025-03-06 11:28:26 -06:00
// Relink the hostinfo as primary
2023-03-13 12:35:14 -05:00
hostinfo . next = oldHostinfo
oldHostinfo . prev = hostinfo
hostinfo . prev = nil
}
2021-04-14 13:50:09 -05:00
func ( hm * HostMap ) unlockedDeleteHostInfo ( hostinfo * HostInfo ) {
2025-03-06 11:28:26 -06:00
for _ , addr := range hostinfo . vpnAddrs {
h := hm . Hosts [ addr ]
for h != nil {
if h == hostinfo {
hm . unlockedInnerDeleteHostInfo ( h , addr )
}
h = h . next
}
}
}
func ( hm * HostMap ) unlockedInnerDeleteHostInfo ( hostinfo * HostInfo , addr netip . Addr ) {
primary , ok := hm . Hosts [ addr ]
isLastHostinfo := hostinfo . next == nil && hostinfo . prev == nil
2023-03-13 12:35:14 -05:00
if ok && primary == hostinfo {
2025-03-06 11:28:26 -06:00
// The vpn addr pointer points to the same hostinfo as the local index id, we can remove it
delete ( hm . Hosts , addr )
2023-03-13 12:35:14 -05:00
if len ( hm . Hosts ) == 0 {
2024-07-31 10:18:56 -05:00
hm . Hosts = map [ netip . Addr ] * HostInfo { }
2023-03-13 12:35:14 -05:00
}
if hostinfo . next != nil {
2025-03-06 11:28:26 -06:00
// We had more than 1 hostinfo at this vpn addr, promote the next in the list to primary
hm . Hosts [ addr ] = hostinfo . next
2023-03-13 12:35:14 -05:00
// It is primary, there is no previous hostinfo now
hostinfo . next . prev = nil
}
} else {
2025-03-06 11:28:26 -06:00
// Relink if we were in the middle of multiple hostinfos for this vpn addr
2023-03-13 12:35:14 -05:00
if hostinfo . prev != nil {
hostinfo . prev . next = hostinfo . next
}
if hostinfo . next != nil {
hostinfo . next . prev = hostinfo . prev
}
2021-03-01 12:40:46 -05:00
}
2023-03-13 12:35:14 -05: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 14:51:16 -05:00
}
2023-03-13 12:35:14 -05:00
2020-11-23 14:51:16 -05:00
delete ( hm . Indexes , hostinfo . localIndexId )
if len ( hm . Indexes ) == 0 {
hm . Indexes = map [ uint32 ] * HostInfo { }
}
2021-03-26 09:46:30 -05:00
if hm . l . Level >= logrus . DebugLevel {
2023-07-24 12:37:52 -05:00
hm . l . WithField ( "hostMap" , m { "mapTotalSize" : len ( hm . Hosts ) ,
2025-03-06 11:28:26 -06:00
"vpnAddrs" : hostinfo . vpnAddrs , "indexNumber" : hostinfo . localIndexId , "remoteIndexNumber" : hostinfo . remoteIndexId } ) .
2020-11-23 14:51:16 -05:00
Debug ( "Hostmap hostInfo deleted" )
}
2023-05-04 15:16:37 -05:00
2025-03-06 11:28:26 -06:00
if isLastHostinfo {
// I have lost connectivity to my peers. My relay tunnel is likely broken. Mark the next
// hops as 'Requested' so that new relay tunnels are created in the future.
hm . unlockedDisestablishVpnAddrRelayFor ( hostinfo )
}
// Clean up any local relay indexes for which I am acting as a relay hop
2023-05-04 15:16:37 -05:00
for _ , localRelayIdx := range hostinfo . relayState . CopyRelayForIdxs ( ) {
delete ( hm . Relays , localRelayIdx )
}
2020-11-23 14:51:16 -05:00
}
2023-07-24 12:37:52 -05:00
func ( hm * HostMap ) QueryIndex ( index uint32 ) * HostInfo {
2019-11-19 17:00:20 +00:00
hm . RLock ( )
if h , ok := hm . Indexes [ index ] ; ok {
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return h
2019-11-19 17:00:20 +00:00
} else {
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return nil
2019-11-19 17:00:20 +00:00
}
}
2023-03-30 12:09:20 -04:00
2023-07-24 12:37:52 -05:00
func ( hm * HostMap ) QueryRelayIndex ( index uint32 ) * HostInfo {
2022-06-21 14:35:23 -04:00
hm . RLock ( )
if h , ok := hm . Relays [ index ] ; ok {
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return h
2022-06-21 14:35:23 -04:00
} else {
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return nil
2022-06-21 14:35:23 -04:00
}
}
2019-11-19 17:00:20 +00:00
2023-07-24 12:37:52 -05:00
func ( hm * HostMap ) QueryReverseIndex ( index uint32 ) * HostInfo {
2019-11-19 17:00:20 +00:00
hm . RLock ( )
2020-11-23 14:51:16 -05:00
if h , ok := hm . RemoteIndexes [ index ] ; ok {
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return h
2020-11-23 14:51:16 -05:00
} else {
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return nil
2019-11-19 17:00:20 +00:00
}
}
2025-03-06 11:28:26 -06:00
func ( hm * HostMap ) QueryVpnAddr ( vpnIp netip . Addr ) * HostInfo {
return hm . queryVpnAddr ( vpnIp , nil )
2019-11-19 17:00:20 +00:00
}
2025-03-06 11:28:26 -06:00
func ( hm * HostMap ) QueryVpnAddrsRelayFor ( targetIps [ ] netip . Addr , relayHostIp netip . Addr ) ( * HostInfo , * Relay , error ) {
2023-05-04 15:16:37 -05:00
hm . RLock ( )
defer hm . RUnlock ( )
h , ok := hm . Hosts [ relayHostIp ]
if ! ok {
return nil , nil , errors . New ( "unable to find host" )
}
2025-03-06 11:28:26 -06:00
2023-05-04 15:16:37 -05:00
for h != nil {
2025-03-06 11:28:26 -06:00
for _ , targetIp := range targetIps {
r , ok := h . relayState . QueryRelayForByIp ( targetIp )
if ok && r . State == Established {
return h , r , nil
}
2023-05-04 15:16:37 -05:00
}
h = h . next
}
2025-03-06 11:28:26 -06:00
2023-05-04 15:16:37 -05:00
return nil , nil , errors . New ( "unable to find host with relay" )
}
2025-03-06 11:28:26 -06:00
func ( hm * HostMap ) unlockedDisestablishVpnAddrRelayFor ( hi * HostInfo ) {
for _ , relayHostIp := range hi . relayState . CopyRelayIps ( ) {
if h , ok := hm . Hosts [ relayHostIp ] ; ok {
for h != nil {
h . relayState . UpdateRelayForByIpState ( hi . vpnAddrs [ 0 ] , Disestablished )
h = h . next
}
}
}
for _ , rs := range hi . relayState . CopyAllRelayFor ( ) {
if rs . Type == ForwardingType {
if h , ok := hm . Hosts [ rs . PeerAddr ] ; ok {
for h != nil {
h . relayState . UpdateRelayForByIpState ( hi . vpnAddrs [ 0 ] , Disestablished )
h = h . next
}
}
}
}
}
func ( hm * HostMap ) queryVpnAddr ( vpnIp netip . Addr , promoteIfce * Interface ) * HostInfo {
2019-11-19 17:00:20 +00:00
hm . RLock ( )
if h , ok := hm . Hosts [ vpnIp ] ; ok {
2021-05-05 13:10:55 -05:00
hm . RUnlock ( )
2021-04-14 13:50:09 -05:00
// Do not attempt promotion if you are a lighthouse
if promoteIfce != nil && ! promoteIfce . lightHouse . amLighthouse {
2024-04-03 22:14:51 -05:00
h . TryPromoteBest ( hm . GetPreferredRanges ( ) , promoteIfce )
2019-11-19 17:00:20 +00:00
}
2023-07-24 12:37:52 -05:00
return h
2021-04-14 13:50:09 -05:00
2019-11-19 17:00:20 +00:00
}
2021-05-05 13:10:55 -05:00
hm . RUnlock ( )
2023-07-24 12:37:52 -05:00
return nil
2019-11-19 17:00:20 +00:00
}
2023-03-13 12:35:14 -05: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 14:16:25 -05:00
if f . serveDns {
2021-04-14 13:50:09 -05:00
remoteCert := hostinfo . ConnectionState . peerCert
2025-03-06 11:28:26 -06:00
dnsR . Add ( remoteCert . Certificate . Name ( ) + "." , hostinfo . vpnAddrs )
2019-11-19 17:00:20 +00:00
}
2025-03-06 11:28:26 -06:00
for _ , addr := range hostinfo . vpnAddrs {
hm . unlockedInnerAddHostInfo ( addr , hostinfo , f )
2023-03-13 12:35:14 -05:00
}
2021-03-12 14:16:25 -05:00
hm . Indexes [ hostinfo . localIndexId ] = hostinfo
hm . RemoteIndexes [ hostinfo . remoteIndexId ] = hostinfo
2019-11-19 17:00:20 +00:00
2021-03-26 09:46:30 -05:00
if hm . l . Level >= logrus . DebugLevel {
2025-03-06 11:28:26 -06:00
hm . l . WithField ( "hostMap" , m { "vpnAddrs" : hostinfo . vpnAddrs , "mapTotalSize" : len ( hm . Hosts ) ,
"hostinfo" : m { "existing" : true , "localIndexId" : hostinfo . localIndexId , "vpnAddrs" : hostinfo . vpnAddrs } } ) .
2021-03-12 14:16:25 -05:00
Debug ( "Hostmap vpnIp added" )
2019-11-19 17:00:20 +00:00
}
2025-03-06 11:28:26 -06:00
}
func ( hm * HostMap ) unlockedInnerAddHostInfo ( vpnAddr netip . Addr , hostinfo * HostInfo , f * Interface ) {
existing := hm . Hosts [ vpnAddr ]
hm . Hosts [ vpnAddr ] = hostinfo
if existing != nil && existing != hostinfo {
hostinfo . next = existing
existing . prev = hostinfo
}
2023-03-13 12:35:14 -05:00
i := 1
check := hostinfo
for check != nil {
if i > MaxHostInfosPerVpnIp {
hm . unlockedDeleteHostInfo ( check )
}
check = check . next
i ++
}
2019-11-19 17:00:20 +00:00
}
2024-07-31 10:18:56 -05:00
func ( hm * HostMap ) GetPreferredRanges ( ) [ ] netip . Prefix {
2024-04-03 22:14:51 -05:00
//NOTE: if preferredRanges is ever not stored before a load this will fail to dereference a nil pointer
return * hm . preferredRanges . Load ( )
2023-07-24 12:37:52 -05:00
}
2025-03-06 11:28:26 -06:00
func ( hm * HostMap ) ForEachVpnAddr ( f controlEach ) {
2023-07-24 12:37:52 -05:00
hm . RLock ( )
defer hm . RUnlock ( )
for _ , v := range hm . Hosts {
f ( v )
}
}
func ( hm * HostMap ) ForEachIndex ( f controlEach ) {
hm . RLock ( )
defer hm . RUnlock ( )
for _ , v := range hm . Indexes {
f ( v )
}
}
2021-04-14 13:50:09 -05: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!
2024-07-31 10:18:56 -05:00
func ( i * HostInfo ) TryPromoteBest ( preferredRanges [ ] netip . Prefix , ifce * Interface ) {
2022-10-31 13:37:41 -04:00
c := i . promoteCounter . Add ( 1 )
2023-08-08 13:26:41 -05:00
if c % ifce . tryPromoteEvery . Load ( ) == 0 {
2022-06-21 14:35:23 -04:00
remote := i . remote
2021-05-05 13:10:55 -05:00
2019-11-19 17:00:20 +00:00
// return early if we are already on a preferred remote
2024-07-31 10:18:56 -05:00
if remote . IsValid ( ) {
rIP := remote . Addr ( )
2022-06-21 14:35:23 -04:00
for _ , l := range preferredRanges {
if l . Contains ( rIP ) {
return
}
2019-11-19 17:00:20 +00:00
}
}
2024-07-31 10:18:56 -05:00
i . remotes . ForEach ( preferredRanges , func ( addr netip . AddrPort , preferred bool ) {
if remote . IsValid ( ) && ( ! addr . IsValid ( ) || ! preferred ) {
2021-04-14 13:50:09 -05:00
return
}
2019-11-19 17:00:20 +00: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 14:35:23 -04:00
ifce . sendTo ( header . Test , header . TestRequest , i . ConnectionState , i , addr , [ ] byte ( "" ) , make ( [ ] byte , 12 , 12 ) , make ( [ ] byte , mtu ) )
2021-04-14 13:50:09 -05:00
} )
2019-11-19 17:00:20 +00:00
}
2021-04-14 13:50:09 -05:00
// Re query our lighthouses for new remotes occasionally
2023-08-08 13:26:41 -05:00
if c % ifce . reQueryEvery . Load ( ) == 0 && ifce . lightHouse != nil {
now := time . Now ( ) . UnixNano ( )
if now < i . nextLHQuery . Load ( ) {
return
}
i . nextLHQuery . Store ( now + ifce . reQueryWait . Load ( ) )
2025-03-06 11:28:26 -06:00
ifce . lightHouse . QueryServer ( i . vpnAddrs [ 0 ] )
2019-11-19 17:00:20 +00:00
}
}
2024-10-10 18:00:22 -05:00
func ( i * HostInfo ) GetCert ( ) * cert . CachedCertificate {
2019-11-19 17:00:20 +00:00
if i . ConnectionState != nil {
return i . ConnectionState . peerCert
}
return nil
}
2025-12-03 18:48:29 -05:00
// TODO: Maybe use ViaSender here?
2024-07-31 10:18:56 -05:00
func ( i * HostInfo ) SetRemote ( remote netip . AddrPort ) {
2021-04-14 13:50:09 -05:00
// We copy here because we likely got this remote from a source that reuses the object
2024-07-31 10:18:56 -05:00
if i . remote != remote {
i . remote = remote
2025-03-06 11:28:26 -06:00
i . remotes . LearnRemote ( i . vpnAddrs [ 0 ] , remote )
2021-03-31 10:26:35 -05:00
}
2019-11-19 17:00:20 +00:00
}
2021-10-19 10:53:55 -04:00
// SetRemoteIfPreferred returns true if the remote was changed. The lastRoam
// time on the HostInfo will also be updated.
2025-12-03 18:48:29 -05:00
func ( i * HostInfo ) SetRemoteIfPreferred ( hm * HostMap , via ViaSender ) bool {
if via . IsRelayed {
2022-06-21 14:35:23 -04:00
return false
}
2025-12-03 18:48:29 -05:00
2021-10-19 10:53:55 -04:00
currentRemote := i . remote
2024-07-31 10:18:56 -05:00
if ! currentRemote . IsValid ( ) {
2025-12-03 18:48:29 -05:00
i . SetRemote ( via . UdpAddr )
2021-10-19 10:53:55 -04:00
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
2024-04-03 22:14:51 -05:00
for _ , l := range hm . GetPreferredRanges ( ) {
2021-10-19 10:53:55 -04:00
// return early if we are already on a preferred remote
2024-07-31 10:18:56 -05:00
if l . Contains ( currentRemote . Addr ( ) ) {
2021-10-19 10:53:55 -04:00
return false
}
2025-12-03 18:48:29 -05:00
if l . Contains ( via . UdpAddr . Addr ( ) ) {
2021-10-19 10:53:55 -04:00
newIsPreferred = true
}
}
if newIsPreferred {
// Consider this a roaming event
i . lastRoam = time . Now ( )
2024-07-31 10:18:56 -05:00
i . lastRoamRemote = currentRemote
2021-10-19 10:53:55 -04:00
2025-12-03 18:48:29 -05:00
i . SetRemote ( via . UdpAddr )
2021-10-19 10:53:55 -04:00
return true
}
return false
}
2025-11-12 13:40:20 -06:00
// buildNetworks fills in the networks field of HostInfo. It accepts a cert.Certificate so you never ever mix the network types up.
func ( i * HostInfo ) buildNetworks ( myVpnNetworksTable * bart . Lite , c cert . Certificate ) {
if len ( c . Networks ( ) ) == 1 && len ( c . UnsafeNetworks ( ) ) == 0 {
if myVpnNetworksTable . Contains ( c . Networks ( ) [ 0 ] . Addr ( ) ) {
return // Simple case, no BART needed
}
2020-03-02 16:21:33 -05:00
}
2025-11-12 13:40:20 -06:00
i . networks = new ( bart . Table [ NetworkType ] )
for _ , network := range c . Networks ( ) {
2025-10-08 17:02:36 +01:00
nprefix := netip . PrefixFrom ( network . Addr ( ) , network . Addr ( ) . BitLen ( ) )
2025-11-12 13:40:20 -06:00
if myVpnNetworksTable . Contains ( network . Addr ( ) ) {
i . networks . Insert ( nprefix , NetworkTypeVPN )
} else {
i . networks . Insert ( nprefix , NetworkTypeVPNPeer )
}
2019-12-12 16:34:17 +00:00
}
2025-11-12 13:40:20 -06:00
for _ , network := range c . UnsafeNetworks ( ) {
i . networks . Insert ( network , NetworkTypeUnsafe )
2019-12-12 16:34:17 +00:00
}
}
2021-03-26 09:46:30 -05:00
func ( i * HostInfo ) logger ( l * logrus . Logger ) * logrus . Entry {
2020-04-06 14:34:00 -04:00
if i == nil {
return logrus . NewEntry ( l )
}
2025-03-06 11:28:26 -06:00
li := l . WithField ( "vpnAddrs" , i . vpnAddrs ) .
2023-02-13 14:41:05 -06:00
WithField ( "localIndex" , i . localIndexId ) .
WithField ( "remoteIndex" , i . remoteIndexId )
2020-04-06 14:34:00 -04:00
if connState := i . ConnectionState ; connState != nil {
if peerCert := connState . peerCert ; peerCert != nil {
2024-10-10 18:00:22 -05:00
li = li . WithField ( "certName" , peerCert . Certificate . Name ( ) )
2020-04-06 14:34:00 -04:00
}
}
return li
}
2019-11-19 17:00:20 +00:00
// Utility functions
2025-03-06 11:28:26 -06:00
func localAddrs ( l * logrus . Logger , allowList * LocalAllowList ) [ ] netip . Addr {
2019-11-19 17:00:20 +00:00
//FIXME: This function is pretty garbage
2025-03-06 11:28:26 -06:00
var finalAddrs [ ] netip . Addr
2019-11-19 17:00:20 +00:00
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 15:36:43 -04:00
allow := allowList . AllowName ( i . Name )
2021-03-31 10:26:35 -05: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 15:36:43 -04:00
if ! allow {
continue
}
2019-11-19 17:00:20 +00:00
addrs , _ := i . Addrs ( )
2025-03-06 11:28:26 -06:00
for _ , rawAddr := range addrs {
var addr netip . Addr
switch v := rawAddr . ( type ) {
2019-11-19 17:00:20 +00:00
case * net . IPNet :
//continue
2025-03-06 11:28:26 -06:00
addr , _ = netip . AddrFromSlice ( v . IP )
2019-11-19 17:00:20 +00:00
case * net . IPAddr :
2025-03-06 11:28:26 -06:00
addr , _ = netip . AddrFromSlice ( v . IP )
2019-11-19 17:00:20 +00:00
}
2021-03-18 20:37:24 -05:00
2025-03-06 11:28:26 -06:00
if ! addr . IsValid ( ) {
2024-07-31 10:18:56 -05:00
if l . Level >= logrus . DebugLevel {
2025-03-06 11:28:26 -06:00
l . WithField ( "localAddr" , rawAddr ) . Debug ( "addr was invalid" )
2024-07-31 10:18:56 -05:00
}
continue
}
2025-03-06 11:28:26 -06:00
addr = addr . Unmap ( )
2024-07-31 10:18:56 -05:00
2025-03-06 11:28:26 -06:00
if addr . IsLoopback ( ) == false && addr . IsLinkLocalUnicast ( ) == false {
isAllowed := allowList . Allow ( addr )
2021-03-31 10:26:35 -05:00
if l . Level >= logrus . TraceLevel {
2025-03-06 11:28:26 -06:00
l . WithField ( "localAddr" , addr ) . WithField ( "allowed" , isAllowed ) . Trace ( "localAllowList.Allow" )
2021-03-31 10:26:35 -05:00
}
2025-03-06 11:28:26 -06:00
if ! isAllowed {
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 15:36:43 -04:00
continue
}
2025-03-06 11:28:26 -06:00
finalAddrs = append ( finalAddrs , addr )
2019-11-19 17:00:20 +00:00
}
}
}
2025-03-06 11:28:26 -06:00
return finalAddrs
2019-11-19 17:00:20 +00:00
}