Browse Source

NM-159: simplify auto assign gw logic (#3719)

* force update host dns field if node is acting as inet gw

* add old acl support checks

* move auto relay migration to pro pkg

* add check to avoid unsetting relayed node

* simplify auto assign gw logic

* send auto assign update on un relay

* set checking time to latest on updates

* fix HA auto Relay logic

* add relay node metrics to peer signal

* move auto relay peer check

* publish host peer update

* check and unset unrelayed auto peers

* use relay node mutex to avoid rac condition

* reset autorelayed peers on auto assign gw
Abhishek K 1 month ago
parent
commit
c643a50b67

+ 1 - 1
controllers/gateway.go

@@ -95,7 +95,7 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
 			if relayedNode.FailedOverBy != uuid.Nil {
 			if relayedNode.FailedOverBy != uuid.Nil {
 				go logic.ResetFailedOverPeer(&relayedNode)
 				go logic.ResetFailedOverPeer(&relayedNode)
 			}
 			}
-			if relayedNode.AutoRelayedBy != uuid.Nil {
+			if len(relayedNode.AutoRelayedPeers) > 0 {
 				go logic.ResetAutoRelayedPeer(&relayedNode)
 				go logic.ResetAutoRelayedPeer(&relayedNode)
 			}
 			}
 
 

+ 13 - 5
controllers/node.go

@@ -6,7 +6,6 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 
 
-	"github.com/google/uuid"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
@@ -642,7 +641,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 			newNode.RelayedBy = ""
 			newNode.RelayedBy = ""
 		}
 		}
 	}
 	}
-	if (currentNode.IsRelayed || currentNode.FailedOverBy != uuid.Nil) && newNode.AutoAssignGateway {
+	if (currentNode.IsRelayed) && newNode.AutoAssignGateway {
 		// if relayed remove it
 		// if relayed remove it
 		if currentNode.IsRelayed {
 		if currentNode.IsRelayed {
 			relayNode, err := logic.GetNodeByID(currentNode.RelayedBy)
 			relayNode, err := logic.GetNodeByID(currentNode.RelayedBy)
@@ -653,7 +652,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 			newNode.IsRelayed = false
 			newNode.IsRelayed = false
 			newNode.RelayedBy = ""
 			newNode.RelayedBy = ""
 		}
 		}
-		if currentNode.FailedOverBy != uuid.Nil {
+		if len(currentNode.AutoRelayedPeers) > 0 {
+			logic.ResetAutoRelayedPeer(&currentNode)
+		}
+	}
+	if !currentNode.AutoAssignGateway && newNode.AutoAssignGateway {
+		if len(currentNode.AutoRelayedPeers) > 0 {
 			logic.ResetAutoRelayedPeer(&currentNode)
 			logic.ResetAutoRelayedPeer(&currentNode)
 		}
 		}
 	}
 	}
@@ -697,10 +701,14 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		// if !newNode.Connected {
 		// if !newNode.Connected {
 		// 	mq.HostUpdate(&models.HostUpdate{Host: *host, Action: models.SignalPull})
 		// 	mq.HostUpdate(&models.HostUpdate{Host: *host, Action: models.SignalPull})
 		// }
 		// }
-		mq.PublishPeerUpdate(false)
-		if newNode.AutoAssignGateway {
+		allNodes, err := logic.GetAllNodes()
+		if err == nil {
+			mq.PublishSingleHostPeerUpdate(host, allNodes, nil, nil, false, nil)
+		}
+		if servercfg.IsPro && newNode.AutoAssignGateway {
 			mq.HostUpdate(&models.HostUpdate{Action: models.CheckAutoAssignGw, Host: *host, Node: *newNode})
 			mq.HostUpdate(&models.HostUpdate{Action: models.CheckAutoAssignGw, Host: *host, Node: *newNode})
 		}
 		}
+		mq.PublishPeerUpdate(false)
 		if servercfg.IsDNSMode() {
 		if servercfg.IsDNSMode() {
 			logic.SetDNS()
 			logic.SetDNS()
 		}
 		}

+ 1 - 1
logic/gateway.go

@@ -374,7 +374,7 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool
 		if clientNode.FailedOverBy != uuid.Nil {
 		if clientNode.FailedOverBy != uuid.Nil {
 			ResetFailedOverPeer(&clientNode)
 			ResetFailedOverPeer(&clientNode)
 		}
 		}
-		if clientNode.AutoRelayedBy != uuid.Nil {
+		if len(clientNode.AutoRelayedPeers) > 0 {
 			ResetAutoRelayedPeer(&clientNode)
 			ResetAutoRelayedPeer(&clientNode)
 		}
 		}
 
 

+ 1 - 1
logic/hosts.go

@@ -345,7 +345,7 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
 			if node.FailedOverBy != uuid.Nil {
 			if node.FailedOverBy != uuid.Nil {
 				ResetFailedOverPeer(&node)
 				ResetFailedOverPeer(&node)
 			}
 			}
-			if node.AutoRelayedBy != uuid.Nil {
+			if len(node.AutoRelayedPeers) > 0 {
 				ResetAutoRelayedPeer(&node)
 				ResetAutoRelayedPeer(&node)
 			}
 			}
 		}
 		}

+ 1 - 1
logic/nodes.go

@@ -282,7 +282,7 @@ func DeleteNode(node *models.Node, purge bool) error {
 	if node.FailedOverBy != uuid.Nil {
 	if node.FailedOverBy != uuid.Nil {
 		ResetFailedOverPeer(node)
 		ResetFailedOverPeer(node)
 	}
 	}
-	if node.AutoRelayedBy != uuid.Nil {
+	if len(node.AutoRelayedPeers) > 0 {
 		ResetAutoRelayedPeer(node)
 		ResetAutoRelayedPeer(node)
 	}
 	}
 	if node.IsRelay {
 	if node.IsRelay {

+ 17 - 24
logic/peers.go

@@ -277,7 +277,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				node.Mutex.Lock()
 				node.Mutex.Lock()
 			}
 			}
 			_, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
 			_, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
-			_, isAutoRelayPeer := node.AutoRelayedPeers[peer.ID.String()]
+			peerAutoRelayID, isAutoRelayPeer := node.AutoRelayedPeers[peer.ID.String()]
 			if node.Mutex != nil {
 			if node.Mutex != nil {
 				node.Mutex.Unlock()
 				node.Mutex.Unlock()
 			}
 			}
@@ -294,9 +294,9 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 						}
 						}
 					}
 					}
 				}
 				}
-				if isAutoRelayPeer && peer.AutoRelayedBy.String() != node.ID.String() {
+				if isAutoRelayPeer && peerAutoRelayID != node.ID.String() {
 					// get relay host
 					// get relay host
-					autoRelayNode, err := GetNodeByID(peer.AutoRelayedBy.String())
+					autoRelayNode, err := GetNodeByID(peerAutoRelayID)
 					if err == nil {
 					if err == nil {
 						relayHost, err := GetHost(autoRelayNode.HostID.String())
 						relayHost, err := GetHost(autoRelayNode.HostID.String())
 						if err == nil {
 						if err == nil {
@@ -345,26 +345,20 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 						peer)
 						peer)
 				}
 				}
 			}
 			}
-			shouldCheckRelayed := true
-			if (node.AutoAssignGateway && peer.IsGw && node.RelayedBy != peer.ID.String()) ||
-				(peer.AutoAssignGateway && node.IsGw && peer.RelayedBy != node.ID.String()) {
-				shouldCheckRelayed = false
-			}
-			if shouldCheckRelayed {
-				if (node.IsRelayed && node.RelayedBy != peer.ID.String()) ||
-					(peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer || isAutoRelayPeer {
-					// if node is relayed and peer is not the relay, set remove to true
-					if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok {
-						continue
-					}
-					peerConfig.Remove = true
-					hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
-					peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
+
+			if (node.IsRelayed && node.RelayedBy != peer.ID.String()) ||
+				(peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer || isAutoRelayPeer {
+				// if node is relayed and peer is not the relay, set remove to true
+				if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok {
 					continue
 					continue
 				}
 				}
-				if node.IsRelayed && node.RelayedBy == peer.ID.String() {
-					hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate)
-				}
+				peerConfig.Remove = true
+				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
+				peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
+				continue
+			}
+			if node.IsRelayed && node.RelayedBy == peer.ID.String() {
+				hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate)
 			}
 			}
 
 
 			uselocal := false
 			uselocal := false
@@ -401,15 +395,14 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 					peerEndpoint = peerHost.EndpointIPv6
 					peerEndpoint = peerHost.EndpointIPv6
 				}
 				}
 			}
 			}
-			if (node.IsRelay && peer.RelayedBy == node.ID.String() && peer.InternetGwID == "") ||
-				(peer.AutoAssignGateway && node.IsGw) && !peer.IsStatic {
+			if node.IsRelay && peer.RelayedBy == node.ID.String() && peer.InternetGwID == "" && !peer.IsStatic {
 				// don't set endpoint on relayed peer
 				// don't set endpoint on relayed peer
 				peerEndpoint = nil
 				peerEndpoint = nil
 			}
 			}
 			if isFailOverPeer && peer.FailedOverBy == node.ID && !peer.IsStatic {
 			if isFailOverPeer && peer.FailedOverBy == node.ID && !peer.IsStatic {
 				peerEndpoint = nil
 				peerEndpoint = nil
 			}
 			}
-			if isAutoRelayPeer && peer.AutoRelayedBy == node.ID && !peer.IsStatic {
+			if isAutoRelayPeer && peerAutoRelayID == node.ID.String() && !peer.IsStatic {
 				peerEndpoint = nil
 				peerEndpoint = nil
 			}
 			}
 
 

+ 1 - 7
logic/relay.go

@@ -142,7 +142,7 @@ func ValidateRelay(relay models.RelayRequest, update bool) error {
 		if relayedNode.FailedOverBy != uuid.Nil {
 		if relayedNode.FailedOverBy != uuid.Nil {
 			ResetFailedOverPeer(&relayedNode)
 			ResetFailedOverPeer(&relayedNode)
 		}
 		}
-		if relayedNode.AutoRelayedBy != uuid.Nil {
+		if len(relayedNode.AutoRelayedPeers) > 0 {
 			ResetAutoRelayedPeer(&relayedNode)
 			ResetAutoRelayedPeer(&relayedNode)
 		}
 		}
 	}
 	}
@@ -212,9 +212,6 @@ func RelayedAllowedIPs(peer, node *models.Node) []net.IPNet {
 		if err != nil {
 		if err != nil {
 			continue
 			continue
 		}
 		}
-		if relayedNode.AutoAssignGateway && node.IsGw {
-			continue
-		}
 		GetNodeEgressInfo(&relayedNode, eli, acls)
 		GetNodeEgressInfo(&relayedNode, eli, acls)
 		allowed := getRelayedAddresses(relayedNodeID)
 		allowed := getRelayedAddresses(relayedNodeID)
 		if relayedNode.EgressDetails.IsEgressGateway {
 		if relayedNode.EgressDetails.IsEgressGateway {
@@ -247,9 +244,6 @@ func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNe
 		if peer.ID == relayed.ID || peer.ID == relay.ID {
 		if peer.ID == relayed.ID || peer.ID == relay.ID {
 			continue
 			continue
 		}
 		}
-		if relayed.AutoAssignGateway && peer.IsGw {
-			continue
-		}
 		if !IsPeerAllowed(*relayed, peer, true) {
 		if !IsPeerAllowed(*relayed, peer, true) {
 			continue
 			continue
 		}
 		}

+ 0 - 14
migrate/migrate.go

@@ -446,20 +446,6 @@ func updateNodes() {
 			node.Tags = make(map[models.TagID]struct{})
 			node.Tags = make(map[models.TagID]struct{})
 			logic.UpsertNode(&node)
 			logic.UpsertNode(&node)
 		}
 		}
-		// deprecate failover  and initialise auto relay fields
-		if node.IsFailOver {
-			node.IsFailOver = false
-			node.FailOverPeers = make(map[string]struct{})
-			node.FailedOverBy = uuid.Nil
-			node.AutoRelayedPeers = make(map[string]struct{})
-			logic.UpsertNode(&node)
-		}
-		if node.FailedOverBy != uuid.Nil || len(node.FailOverPeers) > 0 {
-			node.FailOverPeers = make(map[string]struct{})
-			node.FailedOverBy = uuid.Nil
-			node.AutoRelayedPeers = make(map[string]struct{})
-			logic.UpsertNode(&node)
-		}
 		if node.IsIngressGateway {
 		if node.IsIngressGateway {
 			host, err := logic.GetHost(node.HostID.String())
 			host, err := logic.GetHost(node.HostID.String())
 			if err == nil {
 			if err == nil {

+ 24 - 21
models/api_node.go

@@ -17,26 +17,26 @@ type ApiNodeStatus struct {
 
 
 // ApiNode is a stripped down Node DTO that exposes only required fields to external systems
 // ApiNode is a stripped down Node DTO that exposes only required fields to external systems
 type ApiNode struct {
 type ApiNode struct {
-	ID                            string              `json:"id,omitempty" validate:"required,min=5,id_unique"`
-	HostID                        string              `json:"hostid,omitempty" validate:"required,min=5,id_unique"`
-	Address                       string              `json:"address" validate:"omitempty,cidrv4"`
-	Address6                      string              `json:"address6" validate:"omitempty,cidrv6"`
-	LocalAddress                  string              `json:"localaddress" validate:"omitempty,cidr"`
-	AllowedIPs                    []string            `json:"allowedips"`
-	LastModified                  int64               `json:"lastmodified" swaggertype:"primitive,integer" format:"int64"`
-	ExpirationDateTime            int64               `json:"expdatetime" swaggertype:"primitive,integer" format:"int64"`
-	LastCheckIn                   int64               `json:"lastcheckin" swaggertype:"primitive,integer" format:"int64"`
-	LastPeerUpdate                int64               `json:"lastpeerupdate" swaggertype:"primitive,integer" format:"int64"`
-	Network                       string              `json:"network"`
-	NetworkRange                  string              `json:"networkrange"`
-	NetworkRange6                 string              `json:"networkrange6"`
-	IsRelayed                     bool                `json:"isrelayed"`
-	IsRelay                       bool                `json:"isrelay"`
-	IsGw                          bool                `json:"is_gw"`
-	IsAutoRelay                   bool                `json:"is_auto_relay"`
-	AutoRelayedPeers              map[string]struct{} `json:"auto_relayed_peers"`
-	AutoAssignGateway             bool                `json:"auto_assign_gw"`
-	AutoRelayedBy                 uuid.UUID           `json:"auto_relayed_by"`
+	ID                 string            `json:"id,omitempty" validate:"required,min=5,id_unique"`
+	HostID             string            `json:"hostid,omitempty" validate:"required,min=5,id_unique"`
+	Address            string            `json:"address" validate:"omitempty,cidrv4"`
+	Address6           string            `json:"address6" validate:"omitempty,cidrv6"`
+	LocalAddress       string            `json:"localaddress" validate:"omitempty,cidr"`
+	AllowedIPs         []string          `json:"allowedips"`
+	LastModified       int64             `json:"lastmodified" swaggertype:"primitive,integer" format:"int64"`
+	ExpirationDateTime int64             `json:"expdatetime" swaggertype:"primitive,integer" format:"int64"`
+	LastCheckIn        int64             `json:"lastcheckin" swaggertype:"primitive,integer" format:"int64"`
+	LastPeerUpdate     int64             `json:"lastpeerupdate" swaggertype:"primitive,integer" format:"int64"`
+	Network            string            `json:"network"`
+	NetworkRange       string            `json:"networkrange"`
+	NetworkRange6      string            `json:"networkrange6"`
+	IsRelayed          bool              `json:"isrelayed"`
+	IsRelay            bool              `json:"isrelay"`
+	IsGw               bool              `json:"is_gw"`
+	IsAutoRelay        bool              `json:"is_auto_relay"`
+	AutoRelayedPeers   map[string]string `json:"auto_relayed_peers"`
+	AutoAssignGateway  bool              `json:"auto_assign_gw"`
+	//AutoRelayedBy                 uuid.UUID           `json:"auto_relayed_by"`
 	RelayedBy                     string              `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
 	RelayedBy                     string              `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
 	RelayedNodes                  []string            `json:"relaynodes" yaml:"relayedNodes"`
 	RelayedNodes                  []string            `json:"relaynodes" yaml:"relayedNodes"`
 	IsEgressGateway               bool                `json:"isegressgateway"`
 	IsEgressGateway               bool                `json:"isegressgateway"`
@@ -79,6 +79,9 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	convertedNode.ID, _ = uuid.Parse(a.ID)
 	convertedNode.ID, _ = uuid.Parse(a.ID)
 	convertedNode.HostID, _ = uuid.Parse(a.HostID)
 	convertedNode.HostID, _ = uuid.Parse(a.HostID)
 	//convertedNode.IsRelay = a.IsRelay
 	//convertedNode.IsRelay = a.IsRelay
+	if a.RelayedBy != "" && !a.IsRelayed {
+		a.IsRelayed = true
+	}
 	convertedNode.IsRelayed = a.IsRelayed
 	convertedNode.IsRelayed = a.IsRelayed
 	convertedNode.RelayedBy = a.RelayedBy
 	convertedNode.RelayedBy = a.RelayedBy
 	convertedNode.RelayedNodes = a.RelayedNodes
 	convertedNode.RelayedNodes = a.RelayedNodes
@@ -196,7 +199,7 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
 	apiNode.RelayedBy = nm.RelayedBy
 	apiNode.RelayedBy = nm.RelayedBy
 	apiNode.RelayedNodes = nm.RelayedNodes
 	apiNode.RelayedNodes = nm.RelayedNodes
 	apiNode.IsAutoRelay = nm.IsAutoRelay
 	apiNode.IsAutoRelay = nm.IsAutoRelay
-	apiNode.AutoRelayedBy = nm.AutoRelayedBy
+	//apiNode.AutoRelayedBy = nm.AutoRelayedBy
 	apiNode.AutoRelayedPeers = nm.AutoRelayedPeers
 	apiNode.AutoRelayedPeers = nm.AutoRelayedPeers
 	apiNode.AutoAssignGateway = nm.AutoAssignGateway
 	apiNode.AutoAssignGateway = nm.AutoAssignGateway
 	apiNode.IsIngressGateway = nm.IsIngressGateway
 	apiNode.IsIngressGateway = nm.IsIngressGateway

+ 13 - 12
models/host.go

@@ -160,18 +160,19 @@ type HostTurnRegister struct {
 
 
 // Signal - struct for signalling peer
 // Signal - struct for signalling peer
 type Signal struct {
 type Signal struct {
-	Server         string       `json:"server"`
-	FromHostPubKey string       `json:"from_host_pubkey"`
-	ToHostPubKey   string       `json:"to_host_pubkey"`
-	FromHostID     string       `json:"from_host_id"`
-	ToHostID       string       `json:"to_host_id"`
-	FromNodeID     string       `json:"from_node_id"`
-	ToNodeID       string       `json:"to_node_id"`
-	NetworkID      string       `json:"networkID"`
-	Reply          bool         `json:"reply"`
-	Action         SignalAction `json:"action"`
-	IsPro          bool         `json:"is_pro"`
-	TimeStamp      int64        `json:"timestamp"`
+	Server               string           `json:"server"`
+	FromHostPubKey       string           `json:"from_host_pubkey"`
+	ToHostPubKey         string           `json:"to_host_pubkey"`
+	FromHostID           string           `json:"from_host_id"`
+	ToHostID             string           `json:"to_host_id"`
+	FromNodeID           string           `json:"from_node_id"`
+	ToNodeID             string           `json:"to_node_id"`
+	NetworkID            string           `json:"networkID"`
+	Reply                bool             `json:"reply"`
+	AutoRelayNodeMetrics map[string]int64 `json:"auto_relay_node_metrics"`
+	Action               SignalAction     `json:"action"`
+	IsPro                bool             `json:"is_pro"`
+	TimeStamp            int64            `json:"timestamp"`
 }
 }
 
 
 // RegisterMsg - login message struct for hosts to join via SSO login
 // RegisterMsg - login message struct for hosts to join via SSO login

+ 9 - 8
models/node.go

@@ -106,12 +106,13 @@ type Node struct {
 	IngressMTU                 int32                `json:"ingressmtu"`
 	IngressMTU                 int32                `json:"ingressmtu"`
 	Metadata                   string               `json:"metadata"`
 	Metadata                   string               `json:"metadata"`
 	// == PRO ==
 	// == PRO ==
-	DefaultACL        string              `json:"defaultacl,omitempty" validate:"checkyesornoorunset"`
-	OwnerID           string              `json:"ownerid,omitempty"`
-	IsFailOver        bool                `json:"is_fail_over"`
-	IsAutoRelay       bool                `json:"is_auto_relay"`
-	AutoRelayedPeers  map[string]struct{} `json:"auto_relayed_peers"`
-	AutoRelayedBy     uuid.UUID           `json:"auto_relayed_by"`
+	DefaultACL  string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"`
+	OwnerID     string `json:"ownerid,omitempty"`
+	IsFailOver  bool   `json:"is_fail_over"`
+	IsAutoRelay bool   `json:"is_auto_relay"`
+	//AutoRelayedPeers   map[string]struct{} `json:"auto_relayed_peers"`
+	AutoRelayedPeers map[string]string `json:"auto_relayed_peers_v1"`
+	//AutoRelayedBy     uuid.UUID           `json:"auto_relayed_by"`
 	FailOverPeers     map[string]struct{} `json:"fail_over_peers"`
 	FailOverPeers     map[string]struct{} `json:"fail_over_peers"`
 	FailedOverBy      uuid.UUID           `json:"failed_over_by"`
 	FailedOverBy      uuid.UUID           `json:"failed_over_by"`
 	IsInternetGateway bool                `json:"isinternetgateway"`
 	IsInternetGateway bool                `json:"isinternetgateway"`
@@ -446,10 +447,10 @@ func (newNode *Node) Fill(
 	if newNode.ExpirationDateTime.IsZero() {
 	if newNode.ExpirationDateTime.IsZero() {
 		newNode.ExpirationDateTime = currentNode.ExpirationDateTime
 		newNode.ExpirationDateTime = currentNode.ExpirationDateTime
 	}
 	}
-	if newNode.LastPeerUpdate.IsZero() {
+	if newNode.LastPeerUpdate.IsZero() || currentNode.LastPeerUpdate.After(newNode.LastPeerUpdate) {
 		newNode.LastPeerUpdate = currentNode.LastPeerUpdate
 		newNode.LastPeerUpdate = currentNode.LastPeerUpdate
 	}
 	}
-	if newNode.LastCheckIn.IsZero() {
+	if newNode.LastCheckIn.IsZero() || currentNode.LastCheckIn.After(newNode.LastCheckIn) {
 		newNode.LastCheckIn = currentNode.LastCheckIn
 		newNode.LastCheckIn = currentNode.LastCheckIn
 	}
 	}
 	if newNode.Network == "" {
 	if newNode.Network == "" {

+ 53 - 14
pro/controllers/auto_relay.go

@@ -7,7 +7,6 @@ import (
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 
 
-	"github.com/google/uuid"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 	controller "github.com/gravitl/netmaker/controllers"
 	controller "github.com/gravitl/netmaker/controllers"
 	"github.com/gravitl/netmaker/db"
 	"github.com/gravitl/netmaker/db"
@@ -125,12 +124,11 @@ func resetAutoRelayGw(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	for _, node := range nodes {
 	for _, node := range nodes {
-		if node.AutoRelayedBy != uuid.Nil {
-			node.AutoRelayedBy = uuid.Nil
+		if len(node.AutoRelayedPeers) > 0 {
 			if node.Mutex != nil {
 			if node.Mutex != nil {
 				node.Mutex.Lock()
 				node.Mutex.Lock()
 			}
 			}
-			node.AutoRelayedPeers = make(map[string]struct{})
+			node.AutoRelayedPeers = make(map[string]string)
 			if node.Mutex != nil {
 			if node.Mutex != nil {
 				node.Mutex.Unlock()
 				node.Mutex.Unlock()
 			}
 			}
@@ -374,20 +372,50 @@ func autoRelayMEUpdate(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	if autoRelayReq.AutoRelayGwID == "" {
 	if autoRelayReq.AutoRelayGwID == "" {
-		// unset current gw
-		if node.RelayedBy != "" {
-			// unset relayed node from the curr relay
-			currRelayNode, err := logic.GetNodeByID(node.RelayedBy)
-			if err == nil {
-				newRelayedNodes := logic.RemoveAllFromSlice(currRelayNode.RelayedNodes, node.ID.String())
-				logic.UpdateRelayNodes(currRelayNode.ID.String(), currRelayNode.RelayedNodes, newRelayedNodes)
+		if node.AutoAssignGateway {
+			// unset current gw
+			if node.RelayedBy != "" {
+				// unset relayed node from the curr relay
+				currRelayNode, err := logic.GetNodeByID(node.RelayedBy)
+				if err == nil {
+					if currRelayNode.Mutex != nil {
+						currRelayNode.Mutex.Lock()
+					}
+					newRelayedNodes := logic.RemoveAllFromSlice(currRelayNode.RelayedNodes, node.ID.String())
+					currRelayNode.RelayedNodes = newRelayedNodes
+					logic.UpsertNode(&currRelayNode)
+					node.RelayedBy = ""
+					node.IsRelayed = false
+					logic.UpsertNode(&node)
+					if currRelayNode.Mutex != nil {
+						currRelayNode.Mutex.Unlock()
+					}
+				}
+			}
+		} else {
+			peerNode, err := logic.GetNodeByID(autoRelayReq.NodeID)
+			if err != nil {
+				slog.Error("peer not found: ", "nodeid", autoRelayReq.NodeID, "error", err)
+				logic.ReturnErrorResponse(
+					w,
+					r,
+					logic.FormatError(errors.New("peer not found"), "badrequest"),
+				)
+				return
 			}
 			}
+			delete(node.AutoRelayedPeers, peerNode.ID.String())
+			delete(peerNode.AutoRelayedPeers, node.ID.String())
+			logic.UpsertNode(&node)
+			logic.UpsertNode(&peerNode)
 		}
 		}
 		allNodes, err := logic.GetAllNodes()
 		allNodes, err := logic.GetAllNodes()
 		if err == nil {
 		if err == nil {
 			mq.PublishSingleHostPeerUpdate(host, allNodes, nil, nil, false, nil)
 			mq.PublishSingleHostPeerUpdate(host, allNodes, nil, nil, false, nil)
 		}
 		}
 		go mq.PublishPeerUpdate(false)
 		go mq.PublishPeerUpdate(false)
+		if node.AutoAssignGateway {
+			mq.HostUpdate(&models.HostUpdate{Action: models.CheckAutoAssignGw, Host: *host, Node: node})
+		}
 		logic.ReturnSuccessResponse(w, r, "unrelayed successfully")
 		logic.ReturnSuccessResponse(w, r, "unrelayed successfully")
 		return
 		return
 	}
 	}
@@ -433,7 +461,17 @@ func autoRelayMEUpdate(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnSuccessResponse(w, r, "relayed successfully")
 		logic.ReturnSuccessResponse(w, r, "relayed successfully")
 		return
 		return
 	}
 	}
-	if node.AutoRelayedBy == uuid.Nil {
+	peerNode, err := logic.GetNodeByID(autoRelayReq.NodeID)
+	if err != nil {
+		slog.Error("peer not found: ", "nodeid", autoRelayReq.NodeID, "error", err)
+		logic.ReturnErrorResponse(
+			w,
+			r,
+			logic.FormatError(errors.New("peer not found"), "badrequest"),
+		)
+		return
+	}
+	if len(node.AutoRelayedPeers) == 0 {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is not auto relayed"), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is not auto relayed"), "badrequest"))
 		return
 		return
 	}
 	}
@@ -442,11 +480,12 @@ func autoRelayMEUpdate(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("requested node is not a auto relay node"), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("requested node is not a auto relay node"), "badrequest"))
 		return
 		return
 	}
 	}
-	if node.AutoRelayedBy == autoRelayNode.ID {
+	if node.AutoRelayedPeers[peerNode.ID.String()] == peerNode.AutoRelayedPeers[node.ID.String()] {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("already using requested relay node"), "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("already using requested relay node"), "badrequest"))
 		return
 		return
 	}
 	}
-	node.AutoRelayedBy = autoRelayNode.ID
+	node.AutoRelayedPeers[peerNode.ID.String()] = autoRelayReq.AutoRelayGwID
+	peerNode.AutoRelayedPeers[node.ID.String()] = autoRelayReq.AutoRelayGwID
 	logic.UpsertNode(&node)
 	logic.UpsertNode(&node)
 	slog.Info(
 	slog.Info(
 		"[auto-relay] created relay on node",
 		"[auto-relay] created relay on node",

+ 25 - 30
pro/logic/auto_relay.go

@@ -6,7 +6,6 @@ import (
 	"net"
 	"net"
 	"sync"
 	"sync"
 
 
-	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/db"
 	"github.com/gravitl/netmaker/db"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
@@ -50,19 +49,18 @@ func CheckAutoRelayCtx(autoRelayNode, victimNode, peerNode models.Node) error {
 	if peerNode.Mutex != nil {
 	if peerNode.Mutex != nil {
 		peerNode.Mutex.Lock()
 		peerNode.Mutex.Lock()
 	}
 	}
-	_, peerHasAutoRelayed := peerNode.AutoRelayedPeers[victimNode.ID.String()]
+	autoRelayNodeIDPeerNode, peerHasAutoRelayed := peerNode.AutoRelayedPeers[victimNode.ID.String()]
 	if peerNode.Mutex != nil {
 	if peerNode.Mutex != nil {
 		peerNode.Mutex.Unlock()
 		peerNode.Mutex.Unlock()
 	}
 	}
 	if victimNode.Mutex != nil {
 	if victimNode.Mutex != nil {
 		victimNode.Mutex.Lock()
 		victimNode.Mutex.Lock()
 	}
 	}
-	_, victimHasAutoRelayed := victimNode.AutoRelayedPeers[peerNode.ID.String()]
+	autoRelayNodeIDVictim, victimHasAutoRelayed := victimNode.AutoRelayedPeers[peerNode.ID.String()]
 	if victimNode.Mutex != nil {
 	if victimNode.Mutex != nil {
 		victimNode.Mutex.Unlock()
 		victimNode.Mutex.Unlock()
 	}
 	}
-	if peerHasAutoRelayed && victimHasAutoRelayed &&
-		victimNode.AutoRelayedBy == autoRelayNode.ID && peerNode.AutoRelayedBy == autoRelayNode.ID {
+	if peerHasAutoRelayed && victimHasAutoRelayed && autoRelayNodeIDVictim == autoRelayNodeIDPeerNode {
 		return errors.New("auto relay ctx is already set")
 		return errors.New("auto relay ctx is already set")
 	}
 	}
 	return nil
 	return nil
@@ -71,45 +69,42 @@ func SetAutoRelayCtx(autoRelayNode, victimNode, peerNode models.Node) error {
 	autoRelayCtxMutex.Lock()
 	autoRelayCtxMutex.Lock()
 	defer autoRelayCtxMutex.Unlock()
 	defer autoRelayCtxMutex.Unlock()
 	if peerNode.AutoRelayedPeers == nil {
 	if peerNode.AutoRelayedPeers == nil {
-		peerNode.AutoRelayedPeers = make(map[string]struct{})
+		peerNode.AutoRelayedPeers = make(map[string]string)
 	}
 	}
 	if victimNode.AutoRelayedPeers == nil {
 	if victimNode.AutoRelayedPeers == nil {
-		victimNode.AutoRelayedPeers = make(map[string]struct{})
+		victimNode.AutoRelayedPeers = make(map[string]string)
 	}
 	}
 	if peerNode.Mutex != nil {
 	if peerNode.Mutex != nil {
 		peerNode.Mutex.Lock()
 		peerNode.Mutex.Lock()
 	}
 	}
-	_, peerHasAutoRelayed := peerNode.AutoRelayedPeers[victimNode.ID.String()]
+	autoRelayNodeIDPeerNode, peerHasAutoRelayed := peerNode.AutoRelayedPeers[victimNode.ID.String()]
 	if peerNode.Mutex != nil {
 	if peerNode.Mutex != nil {
 		peerNode.Mutex.Unlock()
 		peerNode.Mutex.Unlock()
 	}
 	}
 	if victimNode.Mutex != nil {
 	if victimNode.Mutex != nil {
 		victimNode.Mutex.Lock()
 		victimNode.Mutex.Lock()
 	}
 	}
-	_, victimHasAutoRelayed := victimNode.AutoRelayedPeers[peerNode.ID.String()]
+	autoRelayNodeIDVictim, victimHasAutoRelayed := victimNode.AutoRelayedPeers[peerNode.ID.String()]
 	if victimNode.Mutex != nil {
 	if victimNode.Mutex != nil {
 		victimNode.Mutex.Unlock()
 		victimNode.Mutex.Unlock()
 	}
 	}
-	if peerHasAutoRelayed && victimHasAutoRelayed &&
-		victimNode.AutoRelayedBy == autoRelayNode.ID && peerNode.AutoRelayedBy == autoRelayNode.ID {
+	if peerHasAutoRelayed && victimHasAutoRelayed && autoRelayNodeIDVictim == autoRelayNodeIDPeerNode {
 		return errors.New("auto relay ctx is already set")
 		return errors.New("auto relay ctx is already set")
 	}
 	}
 	if peerNode.Mutex != nil {
 	if peerNode.Mutex != nil {
 		peerNode.Mutex.Lock()
 		peerNode.Mutex.Lock()
 	}
 	}
-	peerNode.AutoRelayedPeers[victimNode.ID.String()] = struct{}{}
+	peerNode.AutoRelayedPeers[victimNode.ID.String()] = autoRelayNode.ID.String()
 	if peerNode.Mutex != nil {
 	if peerNode.Mutex != nil {
 		peerNode.Mutex.Unlock()
 		peerNode.Mutex.Unlock()
 	}
 	}
 	if victimNode.Mutex != nil {
 	if victimNode.Mutex != nil {
 		victimNode.Mutex.Lock()
 		victimNode.Mutex.Lock()
 	}
 	}
-	victimNode.AutoRelayedPeers[peerNode.ID.String()] = struct{}{}
+	victimNode.AutoRelayedPeers[peerNode.ID.String()] = autoRelayNode.ID.String()
 	if victimNode.Mutex != nil {
 	if victimNode.Mutex != nil {
 		victimNode.Mutex.Unlock()
 		victimNode.Mutex.Unlock()
 	}
 	}
-	victimNode.AutoRelayedBy = autoRelayNode.ID
-	// peerNode.AutoRelayedBy = autoRelayNode.ID
 	if err := logic.UpsertNode(&victimNode); err != nil {
 	if err := logic.UpsertNode(&victimNode); err != nil {
 		return err
 		return err
 	}
 	}
@@ -172,8 +167,7 @@ func ResetAutoRelayedPeer(autoRelayedNode *models.Node) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	autoRelayedNode.AutoRelayedBy = uuid.Nil
-	autoRelayedNode.AutoRelayedPeers = make(map[string]struct{})
+	autoRelayedNode.AutoRelayedPeers = make(map[string]string)
 	err = logic.UpsertNode(autoRelayedNode)
 	err = logic.UpsertNode(autoRelayedNode)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -196,18 +190,16 @@ func ResetAutoRelay(autoRelayNode *models.Node) error {
 		return err
 		return err
 	}
 	}
 	for _, node := range nodes {
 	for _, node := range nodes {
-		if node.AutoRelayedBy == autoRelayNode.ID {
-			node.AutoRelayedBy = uuid.Nil
-			node.AutoRelayedPeers = make(map[string]struct{})
+		for autoRelayedPeerID, autoRelayID := range node.AutoRelayedPeers {
+			if autoRelayID != autoRelayNode.ID.String() {
+				continue
+			}
+			delete(node.AutoRelayedPeers, autoRelayedPeerID)
 			logic.UpsertNode(&node)
 			logic.UpsertNode(&node)
-			for _, peer := range nodes {
-				if peer.ID == node.ID {
-					continue
-				}
-				if _, ok := peer.AutoRelayedPeers[node.ID.String()]; ok {
-					delete(peer.AutoRelayedPeers, node.ID.String())
-					logic.UpsertNode(&peer)
-				}
+			peer, err := logic.GetNodeByID(autoRelayedPeerID)
+			if err == nil {
+				delete(peer.AutoRelayedPeers, node.ID.String())
+				logic.UpsertNode(&peer)
 			}
 			}
 		}
 		}
 	}
 	}
@@ -219,9 +211,12 @@ func GetAutoRelayPeerIps(peer, node *models.Node) []net.IPNet {
 	allowedips := []net.IPNet{}
 	allowedips := []net.IPNet{}
 	eli, _ := (&schema.Egress{Network: node.Network}).ListByNetwork(db.WithContext(context.TODO()))
 	eli, _ := (&schema.Egress{Network: node.Network}).ListByNetwork(db.WithContext(context.TODO()))
 	acls, _ := logic.ListAclsByNetwork(models.NetworkID(node.Network))
 	acls, _ := logic.ListAclsByNetwork(models.NetworkID(node.Network))
-	for autoRelayedpeerID := range node.AutoRelayedPeers {
+	for autoRelayedpeerID, autoRelayID := range node.AutoRelayedPeers {
+		if peer.ID.String() != autoRelayID {
+			continue
+		}
 		autoRelayedpeer, err := logic.GetNodeByID(autoRelayedpeerID)
 		autoRelayedpeer, err := logic.GetNodeByID(autoRelayedpeerID)
-		if err == nil && (autoRelayedpeer.AutoRelayedBy == peer.ID || node.AutoRelayedBy == peer.ID) {
+		if err == nil {
 			logic.GetNodeEgressInfo(&autoRelayedpeer, eli, acls)
 			logic.GetNodeEgressInfo(&autoRelayedpeer, eli, acls)
 			if autoRelayedpeer.Address.IP != nil {
 			if autoRelayedpeer.Address.IP != nil {
 				allowed := net.IPNet{
 				allowed := net.IPNet{

+ 14 - 0
pro/logic/migrate.go

@@ -275,6 +275,20 @@ func MigrateToGws() {
 			delete(node.Tags, models.TagID(fmt.Sprintf("%s.%s", node.Network, models.OldRemoteAccessTagName)))
 			delete(node.Tags, models.TagID(fmt.Sprintf("%s.%s", node.Network, models.OldRemoteAccessTagName)))
 			logic.UpsertNode(&node)
 			logic.UpsertNode(&node)
 		}
 		}
+		// deprecate failover  and initialise auto relay fields
+		if node.IsFailOver {
+			node.IsFailOver = false
+			node.FailOverPeers = make(map[string]struct{})
+			node.FailedOverBy = uuid.Nil
+			node.AutoRelayedPeers = make(map[string]string)
+			logic.UpsertNode(&node)
+		}
+		if node.FailedOverBy != uuid.Nil || len(node.FailOverPeers) > 0 {
+			node.FailOverPeers = make(map[string]struct{})
+			node.FailedOverBy = uuid.Nil
+			node.AutoRelayedPeers = make(map[string]string)
+			logic.UpsertNode(&node)
+		}
 		if node.IsInternetGateway && len(node.InetNodeReq.InetNodeClientIDs) > 0 {
 		if node.IsInternetGateway && len(node.InetNodeReq.InetNodeClientIDs) > 0 {
 			node.RelayedNodes = append(node.RelayedNodes, node.InetNodeReq.InetNodeClientIDs...)
 			node.RelayedNodes = append(node.RelayedNodes, node.InetNodeReq.InetNodeClientIDs...)
 			node.RelayedNodes = logic.UniqueStrings(node.RelayedNodes)
 			node.RelayedNodes = logic.UniqueStrings(node.RelayedNodes)