浏览代码

relay changes

Matthew R Kasun 2 年之前
父节点
当前提交
b33ae9ec27

+ 1 - 0
auth/host_session.go

@@ -237,6 +237,7 @@ func CheckNetRegAndHostUpdate(networks []string, h *models.Host) {
 				Host:   *h,
 				Host:   *h,
 				Node:   *newNode,
 				Node:   *newNode,
 			})
 			})
+			mq.BroadcastAddOrUpdatePeer(h, newNode, false)
 		}
 		}
 	}
 	}
 	if servercfg.IsMessageQueueBackend() {
 	if servercfg.IsMessageQueueBackend() {

+ 0 - 22
cli/cmd/host/create_relay.go

@@ -1,22 +0,0 @@
-package host
-
-import (
-	"strings"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var hostCreateRelayCmd = &cobra.Command{
-	Use:   "create_relay [HOST ID] [RELAYED HOST IDS (comma separated)]",
-	Args:  cobra.ExactArgs(2),
-	Short: "Turn a Host into a Relay",
-	Long:  `Turn a Host into a Relay`,
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.PrettyPrint(functions.CreateRelay(args[0], strings.Split(args[1], ",")))
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(hostCreateRelayCmd)
-}

+ 0 - 20
cli/cmd/host/delete_relay.go

@@ -1,20 +0,0 @@
-package host
-
-import (
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var hostDeleteRelayCmd = &cobra.Command{
-	Use:   "delete_relay [HOST ID]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Delete Relay role from a host",
-	Long:  `Delete Relay role from a host`,
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.PrettyPrint(functions.DeleteRelay(args[0]))
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(hostDeleteRelayCmd)
-}

+ 22 - 0
cli/cmd/node/create_relay.go

@@ -0,0 +1,22 @@
+package node
+
+import (
+	"strings"
+
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var createRelayCmd = &cobra.Command{
+	Use:   "create_relay [NETWORK][NODE ID] [RELAYED NODE IDS (comma separated)]",
+	Args:  cobra.ExactArgs(3),
+	Short: "Turn a Node into a Relay",
+	Long:  `Turn a Node into a Relay`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.CreateRelay(args[0], args[1], strings.Split(args[2], ",")))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(createRelayCmd)
+}

+ 20 - 0
cli/cmd/node/delete_relay.go

@@ -0,0 +1,20 @@
+package node
+
+import (
+	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/spf13/cobra"
+)
+
+var deleteRelayCmd = &cobra.Command{
+	Use:   "delete_relay [NETWORK] [NODE ID]",
+	Args:  cobra.ExactArgs(2),
+	Short: "Delete Relay from a node",
+	Long:  `Delete Relay from a node`,
+	Run: func(cmd *cobra.Command, args []string) {
+		functions.PrettyPrint(functions.DeleteRelay(args[0], args[1]))
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(deleteRelayCmd)
+}

+ 1 - 1
cli/cmd/node/flags.go

@@ -12,7 +12,7 @@ var (
 	postUp                 string
 	postUp                 string
 	postDown               string
 	postDown               string
 	keepAlive              int
 	keepAlive              int
-	relayAddrs             string
+	relayedNodes           string
 	egressGatewayRanges    string
 	egressGatewayRanges    string
 	expirationDateTime     int
 	expirationDateTime     int
 	defaultACL             bool
 	defaultACL             bool

+ 3 - 3
cli/cmd/node/update.go

@@ -35,8 +35,8 @@ var nodeUpdateCmd = &cobra.Command{
 			node.Address6 = address6
 			node.Address6 = address6
 			node.LocalAddress = localAddress
 			node.LocalAddress = localAddress
 			node.PersistentKeepalive = int32(keepAlive)
 			node.PersistentKeepalive = int32(keepAlive)
-			if relayAddrs != "" {
-				node.RelayAddrs = strings.Split(relayAddrs, ",")
+			if relayedNodes != "" {
+				node.RelayedNodes = strings.Split(relayedNodes, ",")
 			}
 			}
 			if egressGatewayRanges != "" {
 			if egressGatewayRanges != "" {
 				node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",")
 				node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",")
@@ -62,7 +62,7 @@ func init() {
 	nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
 	nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
 	nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
 	nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
 	nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
 	nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
-	nodeUpdateCmd.Flags().StringVar(&relayAddrs, "relay_addrs", "", "Addresses for relaying connections if node acts as a relay")
+	nodeUpdateCmd.Flags().StringVar(&relayedNodes, "relayed_nodes", "", "relayed nodes if node acts as a relay")
 	nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
 	nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
 	nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")
 	nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")
 	nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?")
 	nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?")

+ 9 - 8
cli/functions/host.go

@@ -36,17 +36,18 @@ func DeleteHostFromNetwork(hostID, network string) *hostNetworksUpdatePayload {
 	return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil)
 	return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil)
 }
 }
 
 
-// CreateRelay - turn a host into a relay
-func CreateRelay(hostID string, relayedHosts []string) *models.ApiHost {
-	return request[models.ApiHost](http.MethodPost, fmt.Sprintf("/api/hosts/%s/relay", hostID), &models.HostRelayRequest{
-		HostID:       hostID,
-		RelayedHosts: relayedHosts,
+// CreateRelay - add relay to a node
+func CreateRelay(netID, nodeID string, relayedNodes []string) *models.ApiNode {
+	return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", netID, nodeID), &models.RelayRequest{
+		NodeID:       nodeID,
+		NetID:        netID,
+		RelayedNodes: relayedNodes,
 	})
 	})
 }
 }
 
 
-// DeleteRelay - remove relay role from a host
-func DeleteRelay(hostID string) *models.ApiHost {
-	return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s/relay", hostID), nil)
+// DeleteRelay - remove relay from a node
+func DeleteRelay(netID, nodeID string) *models.ApiNode {
+	return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", netID, nodeID), nil)
 }
 }
 
 
 // RefreshKeys - refresh wireguard keys
 // RefreshKeys - refresh wireguard keys

+ 33 - 15
controllers/ext_client.go

@@ -397,8 +397,10 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	logger.Log(0, r.Header.Get("user"), "created new ext client on network", networkName)
 	logger.Log(0, r.Header.Get("user"), "created new ext client on network", networkName)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	go func() {
 	go func() {
-		if err := mq.PublishPeerUpdate(); err != nil {
-			logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
+		mq.BroadcastExtClient(host, &node)
+		f, err := logic.GetFwUpdate(host)
+		if err == nil {
+			mq.PublishFwUpdate(host, &f)
 		}
 		}
 		if err := mq.PublishExtCLientDNS(&extclient); err != nil {
 		if err := mq.PublishExtCLientDNS(&extclient); err != nil {
 			logger.Log(1, "error publishing extclient dns", err.Error())
 			logger.Log(1, "error publishing extclient dns", err.Error())
@@ -484,7 +486,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 	var changedEnabled = (update.Enabled != oldExtClient.Enabled) // indicates there was a change in enablement
 	var changedEnabled = (update.Enabled != oldExtClient.Enabled) // indicates there was a change in enablement
 	// extra var need as logic.Update changes oldExtClient
 	// extra var need as logic.Update changes oldExtClient
 	currentClient := oldExtClient
 	currentClient := oldExtClient
-	newclient, err := logic.UpdateExtClient(&oldExtClient, &update)
+	newclient, replaceOldClient, err := logic.UpdateExtClient(&oldExtClient, &update)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to update ext client [%s], network [%s]: %v",
 			fmt.Sprintf("failed to update ext client [%s], network [%s]: %v",
@@ -493,22 +495,32 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	logger.Log(0, r.Header.Get("user"), "updated ext client", update.ClientID)
 	logger.Log(0, r.Header.Get("user"), "updated ext client", update.ClientID)
-	if changedEnabled { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(newclient)
+
+	go func() {
 		if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
 		if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
-			if err = mq.PublishPeerUpdate(); err != nil {
-				logger.Log(1, "error setting ext peers on", ingressNode.ID.String(), ":", err.Error())
+			if ingressHost, err := logic.GetHost(ingressNode.HostID.String()); err == nil {
+				if replaceOldClient || !update.Enabled {
+					mq.BroadcastDelExtClient(ingressHost, &ingressNode, []models.ExtClient{currentClient})
+				}
+				if replaceOldClient || changedEnabled {
+					// broadcast update
+					mq.BroadcastExtClient(ingressHost, &ingressNode)
+				}
+				f, err := logic.GetFwUpdate(ingressHost)
+				if err == nil {
+					mq.PublishFwUpdate(ingressHost, &f)
+				}
 			}
 			}
 		}
 		}
-	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(newclient)
-	if changedID {
-		go func() {
+		if changedID {
 			if err := mq.PublishExtClientDNSUpdate(currentClient, *newclient, networkName); err != nil {
 			if err := mq.PublishExtClientDNSUpdate(currentClient, *newclient, networkName); err != nil {
 				logger.Log(1, "error pubishing dns update for extcient update", err.Error())
 				logger.Log(1, "error pubishing dns update for extcient update", err.Error())
 			}
 			}
-		}()
-	}
+		}
+	}()
+
 }
 }
 
 
 // swagger:route DELETE /api/extclients/{network}/{clientid} ext_client deleteExtClient
 // swagger:route DELETE /api/extclients/{network}/{clientid} ext_client deleteExtClient
@@ -574,9 +586,15 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 
 
 	go func() {
 	go func() {
-		if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
-			logger.Log(1, "error setting ext peers on "+ingressnode.ID.String()+": "+err.Error())
+		ingressHost, err := logic.GetHost(ingressnode.HostID.String())
+		if err == nil {
+			mq.BroadcastDelExtClient(ingressHost, &ingressnode, []models.ExtClient{extclient})
+			f, err := logic.GetFwUpdate(ingressHost)
+			if err == nil {
+				mq.PublishFwUpdate(ingressHost, &f)
+			}
 		}
 		}
+
 		if err = mq.PublishDeleteExtClientDNS(&extclient); err != nil {
 		if err = mq.PublishDeleteExtClientDNS(&extclient); err != nil {
 			logger.Log(1, "error publishing dns update for extclient deletion", err.Error())
 			logger.Log(1, "error publishing dns update for extclient deletion", err.Error())
 		}
 		}

+ 15 - 55
controllers/hosts.go

@@ -1,12 +1,10 @@
 package controller
 package controller
 
 
 import (
 import (
-	"context"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
-	"reflect"
 
 
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
@@ -26,11 +24,9 @@ func hostHandlers(r *mux.Router) {
 	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(createHostRelay))).Methods(http.MethodPost)
-	r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/host", authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/host/{hostid}/signalpeer", authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/auth-register/host", socketHandler)
 	r.HandleFunc("/api/v1/auth-register/host", socketHandler)
 }
 }
 
 
@@ -114,12 +110,14 @@ func pull(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	hPU, err := logic.GetPeerUpdateForHost(context.Background(), "", host, nil, nil)
-	if err != nil {
-		logger.Log(0, "could not pull peers for host", hostID)
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
+	peers := logic.GetPeerUpdate(host)
+
+	//hPU, err := logic.GetPeerUpdateForHost(context.Background(), "", host, nil, nil)
+	//if err != nil {
+	//logger.Log(0, "could not pull peers for host", hostID)
+	//logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	//return
+	//}
 	serverConf := servercfg.GetServerInfo()
 	serverConf := servercfg.GetServerInfo()
 	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
 	if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
 		serverConf.MQUserName = hostID
 		serverConf.MQUserName = hostID
@@ -130,14 +128,14 @@ func pull(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-
+	fw, _ := logic.GetFwUpdate(host)
 	serverConf.TrafficKey = key
 	serverConf.TrafficKey = key
 	response := models.HostPull{
 	response := models.HostPull{
 		Host:         *host,
 		Host:         *host,
 		Nodes:        logic.GetHostNodes(host),
 		Nodes:        logic.GetHostNodes(host),
 		ServerConfig: serverConf,
 		ServerConfig: serverConf,
-		Peers:        hPU.Peers,
-		PeerIDs:      hPU.PeerIDs,
+		Peers:        peers,
+		FwUpdate:     fw,
 	}
 	}
 
 
 	logger.Log(1, hostID, "completed a pull")
 	logger.Log(1, hostID, "completed a pull")
@@ -174,13 +172,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 
 
 	newHost := newHostData.ConvertAPIHostToNMHost(currHost)
 	newHost := newHostData.ConvertAPIHostToNMHost(currHost)
-	// check if relay information is changed
-	updateRelay := false
-	if newHost.IsRelay && len(newHost.RelayedHosts) > 0 {
-		if len(newHost.RelayedHosts) != len(currHost.RelayedHosts) || !reflect.DeepEqual(newHost.RelayedHosts, currHost.RelayedHosts) {
-			updateRelay = true
-		}
-	}
 
 
 	logic.UpdateHost(newHost, currHost) // update the in memory struct values
 	logic.UpdateHost(newHost, currHost) // update the in memory struct values
 	if err = logic.UpsertHost(newHost); err != nil {
 	if err = logic.UpsertHost(newHost); err != nil {
@@ -188,9 +179,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	if updateRelay {
-		logic.UpdateHostRelay(currHost.ID.String(), currHost.RelayedHosts, newHost.RelayedHosts)
-	}
 	// publish host update through MQ
 	// publish host update through MQ
 	if err := mq.HostUpdate(&models.HostUpdate{
 	if err := mq.HostUpdate(&models.HostUpdate{
 		Action: models.UpdateHost,
 		Action: models.UpdateHost,
@@ -244,33 +232,6 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	if currHost.IsRelay {
-		if _, _, err := logic.DeleteHostRelay(hostid); err != nil {
-			logger.Log(0, r.Header.Get("user"), "failed to dissociate host from relays:", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	}
-	if currHost.IsRelayed {
-		relayHost, err := logic.GetHost(currHost.RelayedBy)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"), "failed to fetch relay host:", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-		newRelayedHosts := make([]string, 0)
-		for _, relayedHostID := range relayHost.RelayedHosts {
-			if relayedHostID != hostid {
-				newRelayedHosts = append(newRelayedHosts, relayedHostID)
-			}
-		}
-		relayHost.RelayedHosts = newRelayedHosts
-		if err := logic.UpsertHost(relayHost); err != nil {
-			logger.Log(0, r.Header.Get("user"), "failed to update host relays:", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	}
 	if err = logic.RemoveHost(currHost); err != nil {
 	if err = logic.RemoveHost(currHost); err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
 		logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -334,6 +295,7 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
 			Action: models.RequestAck,
 			Action: models.RequestAck,
 			Host:   *currHost,
 			Host:   *currHost,
 		})
 		})
+		go mq.BroadcastAddOrUpdatePeer(currHost, newNode, false)
 	}
 	}
 
 
 	logger.Log(2, r.Header.Get("user"), fmt.Sprintf("added host %s to network %s", currHost.Name, network))
 	logger.Log(2, r.Header.Get("user"), fmt.Sprintf("added host %s to network %s", currHost.Name, network))
@@ -385,9 +347,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
 
 
 	runUpdates(node, false)
 	runUpdates(node, false)
 	go func() { // notify of peer change
 	go func() { // notify of peer change
-		if err := mq.PublishPeerUpdate(); err != nil {
-			logger.Log(1, "error publishing peer update ", err.Error())
-		}
+		mq.BroadcastDelPeer(currHost, network)
 		if err := mq.PublishDNSDelete(node, currHost); err != nil {
 		if err := mq.PublishDNSDelete(node, currHost); err != nil {
 			logger.Log(1, "error publishing dns update", err.Error())
 			logger.Log(1, "error publishing dns update", err.Error())
 		}
 		}

+ 2 - 3
controllers/network.go

@@ -143,9 +143,8 @@ func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
 
 
 	// send peer updates
 	// send peer updates
 	if servercfg.IsMessageQueueBackend() {
 	if servercfg.IsMessageQueueBackend() {
-		if err = mq.PublishPeerUpdate(); err != nil {
-			logger.Log(0, "failed to publish peer update after ACL update on", netname)
-		}
+		go mq.BroadcastAclUpdate(netname)
+
 	}
 	}
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(newNetACL)
 	json.NewEncoder(w).Encode(newNetACL)

+ 56 - 71
controllers/node.go

@@ -23,18 +23,16 @@ var hostIDHeader = "host-id"
 
 
 func nodeHandlers(r *mux.Router) {
 func nodeHandlers(r *mux.Router) {
 
 
-	r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
-	r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
+	r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", Authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", Authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
 }
 }
@@ -154,7 +152,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 // even if it's technically ok
 // even if it's technically ok
 // This is kind of a poor man's RBAC. There's probably a better/smarter way.
 // This is kind of a poor man's RBAC. There's probably a better/smarter way.
 // TODO: Consider better RBAC implementations
 // TODO: Consider better RBAC implementations
-func authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
+func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
 		var errorResponse = models.ErrorResponse{
 			Code: http.StatusForbidden, Message: logic.Forbidden_Msg,
 			Code: http.StatusForbidden, Message: logic.Forbidden_Msg,
@@ -461,7 +459,19 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNode)
 	json.NewEncoder(w).Encode(apiNode)
 	go func() {
 	go func() {
-		mq.PublishPeerUpdate()
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			logger.Log(0, "failed to get egress host: ", err.Error())
+			return
+		}
+		mq.BroadcastAddOrUpdatePeer(host, &node, true)
+		f, err := logic.GetFwUpdate(host)
+		if err != nil {
+			logger.Log(0, "failed to get egreess host: ", err.Error())
+			return
+		}
+		mq.PublishFwUpdate(host, &f)
+
 	}()
 	}()
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }
@@ -497,7 +507,20 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNode)
 	json.NewEncoder(w).Encode(apiNode)
 	go func() {
 	go func() {
-		mq.PublishPeerUpdate()
+
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			logger.Log(0, "failed to get egress host: ", err.Error())
+			return
+		}
+		mq.BroadcastAddOrUpdatePeer(host, &node, true)
+		f, err := logic.GetFwUpdate(host)
+		if err != nil {
+			logger.Log(0, "failed to get egreess host: ", err.Error())
+			return
+		}
+		mq.PublishFwUpdate(host, &f)
+
 	}()
 	}()
 	runUpdates(&node, true)
 	runUpdates(&node, true)
 }
 }
@@ -585,12 +608,11 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 	if len(removedClients) > 0 {
 	if len(removedClients) > 0 {
 		host, err := logic.GetHost(node.HostID.String())
 		host, err := logic.GetHost(node.HostID.String())
 		if err == nil {
 		if err == nil {
-			go mq.PublishSingleHostPeerUpdate(
-				context.Background(),
-				host,
-				nil,
-				removedClients[:],
-			)
+			mq.BroadcastDelExtClient(host, &node, removedClients)
+			f, err := logic.GetFwUpdate(host)
+			if err == nil {
+				mq.PublishFwUpdate(host, &f)
+			}
 		}
 		}
 	}
 	}
 
 
@@ -633,12 +655,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	newNode := newData.ConvertToServerNode(&currentNode)
 	newNode := newData.ConvertToServerNode(&currentNode)
 	relayupdate := false
 	relayupdate := false
-	if currentNode.IsRelay && len(newNode.RelayAddrs) > 0 {
-		if len(newNode.RelayAddrs) != len(currentNode.RelayAddrs) {
+	if servercfg.Is_EE && newNode.IsRelay && len(newNode.RelayedNodes) > 0 {
+		if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) {
 			relayupdate = true
 			relayupdate = true
 		} else {
 		} else {
-			for i, addr := range newNode.RelayAddrs {
-				if addr != currentNode.RelayAddrs[i] {
+			for i, node := range newNode.RelayedNodes {
+				if node != currentNode.RelayedNodes[i] {
 					relayupdate = true
 					relayupdate = true
 				}
 				}
 			}
 			}
@@ -651,10 +673,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	relayedUpdate := false
-	if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
-		relayedUpdate = true
-	}
 	ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
 	ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
 	aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
 	aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
 	if ifaceDelta && servercfg.Is_EE {
 	if ifaceDelta && servercfg.Is_EE {
@@ -671,16 +689,13 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	if relayupdate {
 	if relayupdate {
-		updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs)
-		if len(updatenodes) > 0 {
-			for _, relayedNode := range updatenodes {
-				runUpdates(&relayedNode, false)
+		updatedClients := logic.UpdateRelayed(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes)
+		if len(updatedClients) > 0 {
+			for _, relayedClient := range updatedClients {
+				runUpdates(&relayedClient.Node, false)
 			}
 			}
 		}
 		}
 	}
 	}
-	if relayedUpdate {
-		updateRelay(&currentNode, newNode)
-	}
 	if servercfg.IsDNSMode() {
 	if servercfg.IsDNSMode() {
 		logic.SetDNS()
 		logic.SetDNS()
 	}
 	}
@@ -691,11 +706,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(apiNode)
 	json.NewEncoder(w).Encode(apiNode)
 	runUpdates(newNode, ifaceDelta)
 	runUpdates(newNode, ifaceDelta)
 	go func(aclUpdate bool, newNode *models.Node) {
 	go func(aclUpdate bool, newNode *models.Node) {
-		if aclUpdate {
-			if err := mq.PublishPeerUpdate(); err != nil {
-				logger.Log(0, "error during node ACL update for node", newNode.ID.String())
-			}
-		}
+		mq.BroadcastAddOrUpdatePeer(host, newNode, true)
 		if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
 		if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
 			logger.Log(1, "failed to publish dns update", err.Error())
 			logger.Log(1, "failed to publish dns update", err.Error())
 		}
 		}
@@ -750,19 +761,17 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	go func(deletedNode *models.Node, fromNode bool) { // notify of peer change
 	go func(deletedNode *models.Node, fromNode bool) { // notify of peer change
 		var err error
 		var err error
-		if fromNode {
-			err = mq.PublishDeletedNodePeerUpdate(deletedNode)
-		} else {
-			err = mq.PublishPeerUpdate()
-		}
+		host, err := logic.GetHost(node.HostID.String())
 		if err != nil {
 		if err != nil {
-			logger.Log(1, "error publishing peer update ", err.Error())
+			logger.Log(1, "failed to retrieve host for node", node.ID.String(), err.Error())
+			return
 		}
 		}
 
 
-		host, err := logic.GetHost(node.HostID.String())
+		err = mq.BroadcastDelPeer(host, deletedNode.Network)
 		if err != nil {
 		if err != nil {
-			logger.Log(1, "failed to retrieve host for node", node.ID.String(), err.Error())
+			logger.Log(1, "error publishing peer update ", err.Error())
 		}
 		}
+
 		if err := mq.PublishDNSDelete(&node, host); err != nil {
 		if err := mq.PublishDNSDelete(&node, host); err != nil {
 			logger.Log(1, "error publishing dns update", err.Error())
 			logger.Log(1, "error publishing dns update", err.Error())
 		}
 		}
@@ -778,30 +787,6 @@ func runUpdates(node *models.Node, ifaceDelta bool) {
 	}()
 	}()
 }
 }
 
 
-func updateRelay(oldnode, newnode *models.Node) {
-	relay := logic.FindRelay(oldnode)
-	newrelay := relay
-	//check if node's address has been updated and if so, update the relayAddrs of the relay node with the updated address of the relayed node
-	if oldnode.Address.String() != newnode.Address.String() {
-		for i, ip := range newrelay.RelayAddrs {
-			if ip == oldnode.Address.IP.String() {
-				newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...)
-				newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address.IP.String())
-			}
-		}
-	}
-	//check if node's address(v6) has been updated and if so, update the relayAddrs of the relay node with the updated address(v6) of the relayed node
-	if oldnode.Address6.String() != newnode.Address6.String() {
-		for i, ip := range newrelay.RelayAddrs {
-			if ip == oldnode.Address.IP.String() {
-				newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...)
-				newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6.IP.String())
-			}
-		}
-	}
-	logic.UpdateNode(relay, newrelay)
-}
-
 func doesUserOwnNode(username, network, nodeID string) bool {
 func doesUserOwnNode(username, network, nodeID string) bool {
 	u, err := logic.GetUser(username)
 	u, err := logic.GetUser(username)
 	if err != nil {
 	if err != nil {

+ 0 - 199
controllers/relay.go

@@ -1,199 +0,0 @@
-package controller
-
-import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-
-	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/mq"
-)
-
-// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay
-//
-// Create a relay.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: nodeResponse
-func createRelay(w http.ResponseWriter, r *http.Request) {
-	var relay models.RelayRequest
-	var params = mux.Vars(r)
-	w.Header().Set("Content-Type", "application/json")
-	err := json.NewDecoder(r.Body).Decode(&relay)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	relay.NetID = params["network"]
-	relay.NodeID = params["nodeid"]
-	updatenodes, node, err := logic.CreateRelay(relay)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relay.NodeID, relay.NetID, err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
-	for _, relayedNode := range updatenodes {
-
-		err = mq.NodeUpdate(&relayedNode)
-		if err != nil {
-			logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", relay.NetID, ": ", err.Error())
-		}
-	}
-
-	apiNode := node.ConvertToAPINode()
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(apiNode)
-	runUpdates(&node, true)
-}
-
-// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay
-//
-// Remove a relay.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: nodeResponse
-func deleteRelay(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	nodeid := params["nodeid"]
-	netid := params["network"]
-	updatenodes, node, err := logic.DeleteRelay(netid, nodeid)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid)
-	for _, relayedNode := range updatenodes {
-		err = mq.NodeUpdate(&relayedNode)
-		if err != nil {
-			logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", netid, ": ", err.Error())
-		}
-	}
-	apiNode := node.ConvertToAPINode()
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(apiNode)
-	runUpdates(&node, true)
-}
-
-// swagger:route POST /api/hosts/{hostid}/relay hosts createHostRelay
-//
-// Create a relay.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: nodeResponse
-func createHostRelay(w http.ResponseWriter, r *http.Request) {
-	var relay models.HostRelayRequest
-	var params = mux.Vars(r)
-	w.Header().Set("Content-Type", "application/json")
-	err := json.NewDecoder(r.Body).Decode(&relay)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	relay.HostID = params["hostid"]
-	relayHost, relayedHosts, err := logic.CreateHostRelay(relay)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"),
-			fmt.Sprintf("failed to create relay on host [%s]: %v", relay.HostID, err))
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	if err := mq.HostUpdate(&models.HostUpdate{
-		Action: models.UpdateHost,
-		Host:   *relayHost,
-	}); err != nil {
-		logger.Log(0, "failed to send host update: ", relayHost.ID.String(), err.Error())
-	}
-	logger.Log(1, r.Header.Get("user"), "created relay on host", relay.HostID)
-	go func(relayHostID string) {
-		for _, relayedHost := range relayedHosts {
-			relayedHost.ProxyEnabled = true
-			logic.UpsertHost(&relayedHost)
-			if err := mq.HostUpdate(&models.HostUpdate{
-				Action: models.UpdateHost,
-				Host:   relayedHost,
-			}); err != nil {
-				logger.Log(0, "failed to send host update: ", relayedHost.ID.String(), err.Error())
-			}
-		}
-		if err := mq.PublishPeerUpdate(); err != nil {
-			logger.Log(0, "fail to publish peer update: ", err.Error())
-		}
-
-	}(relay.HostID)
-
-	apiHostData := relayHost.ConvertNMHostToAPI()
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(apiHostData)
-}
-
-// swagger:route DELETE /api/hosts/{hostid}/relay hosts deleteHostRelay
-//
-// Remove a relay.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: nodeResponse
-func deleteHostRelay(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	hostid := params["hostid"]
-	relayHost, relayed, err := logic.DeleteHostRelay(hostid)
-	if err != nil {
-		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	logger.Log(1, r.Header.Get("user"), "deleted relay host", hostid)
-	go func() {
-		if err := mq.PublishPeerUpdate(); err != nil {
-			logger.Log(0, "fail to publish peer update: ", err.Error())
-		}
-		if err := mq.HostUpdate(&models.HostUpdate{
-			Action: models.UpdateHost,
-			Host:   *relayHost,
-		}); err != nil {
-			logger.Log(0, "failed to send host update: ", relayHost.Name, err.Error())
-		}
-		for _, relayedHost := range relayed {
-			if err := mq.HostUpdate(&models.HostUpdate{
-				Action: models.UpdateHost,
-				Host:   relayedHost,
-			}); err != nil {
-				logger.Log(0, "failed to send host update: ", relayedHost.Name, err.Error())
-			}
-		}
-	}()
-	apiHostData := relayHost.ConvertNMHostToAPI()
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(apiHostData)
-}

+ 1 - 1
controllers/server.go

@@ -20,7 +20,7 @@ func serverHandlers(r *mux.Router) {
 		resp.Write([]byte("Server is up and running!!"))
 		resp.Write([]byte("Server is up and running!!"))
 	}))
 	}))
 	r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet)
-	r.HandleFunc("/api/server/getserverinfo", authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet)
+	r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet)
 }
 }
 
 

+ 137 - 0
ee/ee_controllers/relay.go

@@ -0,0 +1,137 @@
+package ee_controllers
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	"github.com/gorilla/mux"
+	controller "github.com/gravitl/netmaker/controllers"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+)
+
+// RelayHandlers - handle EE Relays
+func RelayHandlers(r *mux.Router) {
+
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", controller.Authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
+}
+
+// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay
+//
+// Create a relay.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: nodeResponse
+func createRelay(w http.ResponseWriter, r *http.Request) {
+	var relayRequest models.RelayRequest
+	var params = mux.Vars(r)
+	w.Header().Set("Content-Type", "application/json")
+	err := json.NewDecoder(r.Body).Decode(&relayRequest)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	relayRequest.NetID = params["network"]
+	relayRequest.NodeID = params["nodeid"]
+	_, relayNode, err := logic.CreateRelay(relayRequest)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	relayHost, err := logic.GetHost(relayNode.HostID.String())
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("failed to retrieve host for node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	relay := models.Client{
+		Host: *relayHost,
+		Node: relayNode,
+	}
+	peers, err := logic.GetNetworkClients(relay.Node.Network)
+	if err != nil {
+		logger.Log(0, "error getting network nodes: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	//mq.PubPeersforRelay(relay, peers)
+	//for _, relayed := range relayedClients {
+	//mq.PubPeersForRelayedNode(relayed, relay, peers)
+	//}
+	clients := peers
+	for _, client := range clients {
+		mq.PubPeerUpdate(&client, &relay, peers)
+	}
+	logger.Log(1, r.Header.Get("user"), "created relay on node", relayRequest.NodeID, "on network", relayRequest.NetID)
+	apiNode := relayNode.ConvertToAPINode()
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(apiNode)
+}
+
+// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay
+//
+// Remove a relay.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: nodeResponse
+func deleteRelay(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	nodeid := params["nodeid"]
+	netid := params["network"]
+	updateClients, node, err := logic.DeleteRelay(netid, nodeid)
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid)
+	go func() {
+		//update relayHost node
+		relayHost, err := logic.GetHost(node.HostID.String())
+		if err == nil {
+			if err := mq.NodeUpdate(&node); err != nil {
+				logger.Log(1, "relay node update", relayHost.Name, "on network", node.Network, ": ", err.Error())
+			}
+			for _, relayedClient := range updateClients {
+				err = mq.NodeUpdate(&relayedClient.Node)
+				if err != nil {
+					logger.Log(1, "relayed node update ", relayedClient.Node.ID.String(), "on network", relayedClient.Node.Network, ": ", err.Error())
+
+				}
+			}
+			peers, err := logic.GetNetworkClients(node.Network)
+			if err != nil {
+				logger.Log(0, "error getting network nodes: ", err.Error())
+				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+				return
+			}
+			clients := peers
+			for _, client := range clients {
+				mq.PubPeerUpdate(&client, nil, peers)
+			}
+		}
+	}()
+	logger.Log(1, r.Header.Get("user"), "deleted relay on node", node.ID.String(), "on network", node.Network)
+	apiNode := node.ConvertToAPINode()
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(apiNode)
+}

+ 1 - 0
ee/initialize.go

@@ -22,6 +22,7 @@ func InitEE() {
 		ee_controllers.MetricHandlers,
 		ee_controllers.MetricHandlers,
 		ee_controllers.NetworkUsersHandlers,
 		ee_controllers.NetworkUsersHandlers,
 		ee_controllers.UserGroupsHandlers,
 		ee_controllers.UserGroupsHandlers,
+		ee_controllers.RelayHandlers,
 	)
 	)
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 		// == License Handling ==
 		// == License Handling ==

+ 24 - 44
logic/nodes.go

@@ -53,6 +53,26 @@ func GetHostNodes(host *models.Host) []models.Node {
 	return nodes
 	return nodes
 }
 }
 
 
+// GetNetworkClients - gets the clients of a network
+func GetNetworkClients(network string) ([]models.Client, error) {
+	clients := []models.Client{}
+	nodes, err := GetNetworkNodes(network)
+	if err != nil {
+		return []models.Client{}, err
+	}
+	for _, node := range nodes {
+		host, err := GetHost(node.HostID.String())
+		if err == nil {
+			client := models.Client{
+				Node: node,
+				Host: *host,
+			}
+			clients = append(clients, client)
+		}
+	}
+	return clients, nil
+}
+
 // GetNetworkNodesMemory - gets all nodes belonging to a network from list in memory
 // GetNetworkNodesMemory - gets all nodes belonging to a network from list in memory
 func GetNetworkNodesMemory(allNodes []models.Node, network string) []models.Node {
 func GetNetworkNodesMemory(allNodes []models.Node, network string) []models.Node {
 	var nodes = []models.Node{}
 	var nodes = []models.Node{}
@@ -85,7 +105,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 		}
 		}
 	}
 	}
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
-	newNode.Fill(currentNode)
+	newNode.Fill(currentNode, servercfg.Is_EE)
 
 
 	// check for un-settable server values
 	// check for un-settable server values
 	if err := ValidateNode(newNode, true); err != nil {
 	if err := ValidateNode(newNode, true); err != nil {
@@ -338,34 +358,6 @@ func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node,
 	return node, nil
 	return node, nil
 }
 }
 
 
-// GetNodeRelay - gets the relay node of a given network
-func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	var relay models.Node
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return relay, nil
-		}
-		logger.Log(2, err.Error())
-		return relay, err
-	}
-	for _, value := range collection {
-		err := json.Unmarshal([]byte(value), &relay)
-		if err != nil {
-			logger.Log(2, err.Error())
-			continue
-		}
-		if relay.IsRelay {
-			for _, addr := range relay.RelayAddrs {
-				if addr == relayedNodeAddr {
-					return relay, nil
-				}
-			}
-		}
-	}
-	return relay, errors.New(RELAY_NODE_ERR + " " + relayedNodeAddr)
-}
-
 func GetNodeByID(uuid string) (models.Node, error) {
 func GetNodeByID(uuid string) (models.Node, error) {
 	var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid)
 	var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid)
 	if err != nil {
 	if err != nil {
@@ -399,24 +391,12 @@ func GetDeletedNodeByID(uuid string) (models.Node, error) {
 
 
 // FindRelay - returns the node that is the relay for a relayed node
 // FindRelay - returns the node that is the relay for a relayed node
 func FindRelay(node *models.Node) *models.Node {
 func FindRelay(node *models.Node) *models.Node {
-	if !node.IsRelayed {
-		return nil
-	}
-	peers, err := GetNetworkNodes(node.Network)
+	relay, err := GetNodeByID(node.RelayedBy)
 	if err != nil {
 	if err != nil {
+		logger.Log(0, "FindRelay: "+err.Error())
 		return nil
 		return nil
 	}
 	}
-	for _, peer := range peers {
-		if !peer.IsRelay {
-			continue
-		}
-		for _, ip := range peer.RelayAddrs {
-			if ip == node.Address.IP.String() || ip == node.Address6.IP.String() {
-				return &peer
-			}
-		}
-	}
-	return nil
+	return &relay
 }
 }
 
 
 // GetNetworkIngresses - gets the gateways of a network
 // GetNetworkIngresses - gets the gateways of a network

+ 361 - 156
logic/peers.go

@@ -29,41 +29,6 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy
 		Action: models.ProxyUpdate,
 		Action: models.ProxyUpdate,
 	}
 	}
 	peerConfMap := make(map[string]models.PeerConf)
 	peerConfMap := make(map[string]models.PeerConf)
-	if host.IsRelayed {
-		relayHost, err := GetHost(host.RelayedBy)
-		if err == nil {
-			relayEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, GetPeerListenPort(relayHost)))
-			if err != nil {
-				logger.Log(1, "failed to resolve relay node endpoint: ", err.Error())
-			}
-			proxyPayload.IsRelayed = true
-			proxyPayload.RelayedTo = relayEndpoint
-		} else {
-			logger.Log(0, "couldn't find relay host for:  ", host.ID.String())
-		}
-	}
-	if host.IsRelay {
-		relayedHosts := GetRelayedHosts(host)
-		relayPeersMap := make(map[string]models.RelayedConf)
-		for _, relayedHost := range relayedHosts {
-			relayedHost := relayedHost
-			payload, err := GetPeerUpdateForHost(ctx, "", &relayedHost, nil, nil)
-			if err == nil {
-				relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, GetPeerListenPort(&relayedHost)))
-				if udpErr == nil {
-					relayPeersMap[relayedHost.PublicKey.String()] = models.RelayedConf{
-						RelayedPeerEndpoint: relayedEndpoint,
-						RelayedPeerPubKey:   relayedHost.PublicKey.String(),
-						Peers:               payload.Peers,
-					}
-				}
-
-			}
-		}
-		proxyPayload.IsRelay = true
-		proxyPayload.RelayedPeerConf = relayPeersMap
-
-	}
 	var ingressStatus bool
 	var ingressStatus bool
 	for _, nodeID := range host.Nodes {
 	for _, nodeID := range host.Nodes {
 
 
@@ -101,18 +66,6 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy
 				}
 				}
 			}
 			}
 
 
-			if peerHost.IsRelayed && peerHost.RelayedBy != host.ID.String() {
-				relayHost, err := GetHost(peerHost.RelayedBy)
-				if err == nil {
-					relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, GetPeerListenPort(relayHost)))
-					if err == nil {
-						currPeerConf.IsRelayed = true
-						currPeerConf.RelayedTo = relayTo
-					}
-
-				}
-			}
-
 			peerConfMap[peerHost.PublicKey.String()] = currPeerConf
 			peerConfMap[peerHost.PublicKey.String()] = currPeerConf
 		}
 		}
 		if node.IsIngressGateway {
 		if node.IsIngressGateway {
@@ -152,15 +105,11 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 	// track which nodes are deleted
 	// track which nodes are deleted
 	// after peer calculation, if peer not in list, add delete config of peer
 	// after peer calculation, if peer not in list, add delete config of peer
 	hostPeerUpdate := models.HostPeerUpdate{
 	hostPeerUpdate := models.HostPeerUpdate{
-		Host:          *host,
-		Server:        servercfg.GetServer(),
-		HostPeerIDs:   make(models.HostPeerMap, 0),
-		ServerVersion: servercfg.GetVersion(),
-		ServerAddrs:   []models.ServerAddr{},
-		IngressInfo: models.IngressInfo{
-			ExtPeers: make(map[string]models.ExtClientInfo),
-		},
-		EgressInfo:      make(map[string]models.EgressInfo),
+		Host:            *host,
+		Server:          servercfg.GetServer(),
+		HostPeerIDs:     make(models.HostPeerMap, 0),
+		ServerVersion:   servercfg.GetVersion(),
+		ServerAddrs:     []models.ServerAddr{},
 		PeerIDs:         make(models.PeerMap, 0),
 		PeerIDs:         make(models.PeerMap, 0),
 		Peers:           []wgtypes.PeerConfig{},
 		Peers:           []wgtypes.PeerConfig{},
 		NodePeers:       []wgtypes.PeerConfig{},
 		NodePeers:       []wgtypes.PeerConfig{},
@@ -168,7 +117,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 	}
 	}
 
 
 	// endpoint detection always comes from the server
 	// endpoint detection always comes from the server
-	hostPeerUpdate.Host.EndpointDetection = servercfg.EndpointDetectionEnabled()
+	//hostPeerUpdate.Host.EndpointDetection = servercfg.EndpointDetectionEnabled()
 
 
 	logger.Log(1, "peer update for host", host.ID.String())
 	logger.Log(1, "peer update for host", host.ID.String())
 	peerIndexMap := make(map[string]int)
 	peerIndexMap := make(map[string]int)
@@ -182,10 +131,6 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 			continue
 			continue
 		}
 		}
 		currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
 		currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
-		var nodePeerMap map[string]models.PeerRouteInfo
-		if node.IsIngressGateway || node.IsEgressGateway {
-			nodePeerMap = make(map[string]models.PeerRouteInfo)
-		}
 		for _, peer := range currentPeers {
 		for _, peer := range currentPeers {
 			select {
 			select {
 			case <-ctx.Done():
 			case <-ctx.Done():
@@ -198,6 +143,10 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 					//skip yourself
 					//skip yourself
 					continue
 					continue
 				}
 				}
+				if peer.IsRelayed {
+					// skip relayed peers; will be included in relay peer
+					continue
+				}
 				var peerConfig wgtypes.PeerConfig
 				var peerConfig wgtypes.PeerConfig
 				peerHost, err := GetHost(peer.HostID.String())
 				peerHost, err := GetHost(peer.HostID.String())
 				if err != nil {
 				if err != nil {
@@ -240,7 +189,14 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 					}
 					}
 				}
 				}
 				if peer.IsEgressGateway {
 				if peer.IsEgressGateway {
-					allowedips = append(allowedips, getEgressIPs(&node, &peer)...)
+					host, err := GetHost(peer.HostID.String())
+					if err == nil {
+						allowedips = append(allowedips, getEgressIPs(
+							&models.Client{
+								Host: *host,
+								Node: peer,
+							})...)
+					}
 				}
 				}
 				if peer.Action != models.NODE_DELETE &&
 				if peer.Action != models.NODE_DELETE &&
 					!peer.PendingDelete &&
 					!peer.PendingDelete &&
@@ -250,39 +206,6 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 					peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
 					peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
 				}
 				}
 
 
-				if node.IsIngressGateway || node.IsEgressGateway {
-					if peer.IsIngressGateway {
-						_, extPeerIDAndAddrs, err := getExtPeers(&peer)
-						if err == nil {
-							for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
-								extPeerIdAndAddr := extPeerIdAndAddr
-								nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
-									PeerAddr: net.IPNet{
-										IP:   net.ParseIP(extPeerIdAndAddr.Address),
-										Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
-									},
-									PeerKey: extPeerIdAndAddr.ID,
-									Allow:   true,
-									ID:      extPeerIdAndAddr.ID,
-								}
-							}
-						}
-					}
-					if node.IsIngressGateway && peer.IsEgressGateway {
-						hostPeerUpdate.IngressInfo.EgressRanges = append(hostPeerUpdate.IngressInfo.EgressRanges,
-							peer.EgressGatewayRanges...)
-					}
-					nodePeerMap[peerHost.PublicKey.String()] = models.PeerRouteInfo{
-						PeerAddr: net.IPNet{
-							IP:   net.ParseIP(peer.PrimaryAddress()),
-							Mask: getCIDRMaskFromAddr(peer.PrimaryAddress()),
-						},
-						PeerKey: peerHost.PublicKey.String(),
-						Allow:   true,
-						ID:      peer.ID.String(),
-					}
-				}
-
 				peerProxyPort := GetProxyListenPort(peerHost)
 				peerProxyPort := GetProxyListenPort(peerHost)
 				var nodePeer wgtypes.PeerConfig
 				var nodePeer wgtypes.PeerConfig
 				if _, ok := hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()]; !ok {
 				if _, ok := hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()]; !ok {
@@ -334,20 +257,8 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 		var extPeers []wgtypes.PeerConfig
 		var extPeers []wgtypes.PeerConfig
 		var extPeerIDAndAddrs []models.IDandAddr
 		var extPeerIDAndAddrs []models.IDandAddr
 		if node.IsIngressGateway {
 		if node.IsIngressGateway {
-			extPeers, extPeerIDAndAddrs, err = getExtPeers(&node)
+			extPeers, extPeerIDAndAddrs, err = GetExtPeers(&node)
 			if err == nil {
 			if err == nil {
-				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
-					extPeerIdAndAddr := extPeerIdAndAddr
-					nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
-						PeerAddr: net.IPNet{
-							IP:   net.ParseIP(extPeerIdAndAddr.Address),
-							Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
-						},
-						PeerKey: extPeerIdAndAddr.ID,
-						Allow:   true,
-						ID:      extPeerIdAndAddr.ID,
-					}
-				}
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
 				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
 				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
 					extPeerIdAndAddr := extPeerIdAndAddr
 					extPeerIdAndAddr := extPeerIdAndAddr
@@ -358,21 +269,6 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 						Name:    extPeerIdAndAddr.Name,
 						Name:    extPeerIdAndAddr.Name,
 						Network: node.Network,
 						Network: node.Network,
 					}
 					}
-
-					hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
-						Masquerade: true,
-						IngGwAddr: net.IPNet{
-							IP:   net.ParseIP(node.PrimaryAddress()),
-							Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
-						},
-						Network: node.PrimaryNetworkRange(),
-						ExtPeerAddr: net.IPNet{
-							IP:   net.ParseIP(extPeerIdAndAddr.Address),
-							Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
-						},
-						ExtPeerKey: extPeerIdAndAddr.ID,
-						Peers:      filterNodeMapForClientACLs(extPeerIdAndAddr.ID, node.Network, nodePeerMap),
-					}
 					if node.Network == network {
 					if node.Network == network {
 						hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr
 						hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr
 						hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, extPeers...)
 						hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, extPeers...)
@@ -382,18 +278,6 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
 				logger.Log(1, "error retrieving external clients:", err.Error())
 				logger.Log(1, "error retrieving external clients:", err.Error())
 			}
 			}
 		}
 		}
-		if node.IsEgressGateway {
-			hostPeerUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
-				EgressID: node.ID.String(),
-				Network:  node.PrimaryNetworkRange(),
-				EgressGwAddr: net.IPNet{
-					IP:   net.ParseIP(node.PrimaryAddress()),
-					Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
-				},
-				GwPeers:     nodePeerMap,
-				EgressGWCfg: node.EgressGatewayRequest,
-			}
-		}
 	}
 	}
 	// == post peer calculations ==
 	// == post peer calculations ==
 	// indicate removal if no allowed IPs were calculated
 	// indicate removal if no allowed IPs were calculated
@@ -438,6 +322,133 @@ func getPeerWgListenPort(host *models.Host) int {
 	return peerPort
 	return peerPort
 }
 }
 
 
+// GetFwUpdate - fetches the firewall update for the gateway nodes on the host
+func GetFwUpdate(host *models.Host) (models.FwUpdate, error) {
+	fwUpdate := models.FwUpdate{
+		IngressInfo: models.IngressInfo{
+			ExtPeers: make(map[string]models.ExtClientInfo),
+		},
+		EgressInfo: make(map[string]models.EgressInfo),
+	}
+	allNodes, err := GetAllNodes()
+	if err != nil {
+		return fwUpdate, err
+	}
+	for _, nodeID := range host.Nodes {
+		nodeID := nodeID
+		node, err := GetNodeByID(nodeID)
+		if err != nil {
+			continue
+		}
+		if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
+			continue
+		}
+		currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
+		var nodePeerMap map[string]models.PeerRouteInfo
+		if node.IsIngressGateway || node.IsEgressGateway {
+			nodePeerMap = make(map[string]models.PeerRouteInfo)
+		}
+		for _, peer := range currentPeers {
+			peer := peer
+			if peer.ID.String() == node.ID.String() {
+				logger.Log(2, "fw update, skipping self")
+				//skip yourself
+				continue
+			}
+			peerHost, err := GetHost(peer.HostID.String())
+			if err != nil {
+				logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
+				continue
+			}
+			if node.IsIngressGateway || node.IsEgressGateway {
+				if peer.IsIngressGateway {
+					_, extPeerIDAndAddrs, err := GetExtPeers(&peer)
+					if err == nil {
+						for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
+							extPeerIdAndAddr := extPeerIdAndAddr
+							nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
+								PeerAddr: net.IPNet{
+									IP:   net.ParseIP(extPeerIdAndAddr.Address),
+									Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
+								},
+								PeerKey: extPeerIdAndAddr.ID,
+								Allow:   true,
+								ID:      extPeerIdAndAddr.ID,
+							}
+						}
+					}
+				}
+				if node.IsIngressGateway && peer.IsEgressGateway {
+					fwUpdate.IngressInfo.EgressRanges = append(fwUpdate.IngressInfo.EgressRanges,
+						peer.EgressGatewayRanges...)
+				}
+				nodePeerMap[peerHost.PublicKey.String()] = models.PeerRouteInfo{
+					PeerAddr: net.IPNet{
+						IP:   net.ParseIP(peer.PrimaryAddress()),
+						Mask: getCIDRMaskFromAddr(peer.PrimaryAddress()),
+					},
+					PeerKey: peerHost.PublicKey.String(),
+					Allow:   true,
+					ID:      peer.ID.String(),
+				}
+			}
+		}
+		var extPeerIDAndAddrs []models.IDandAddr
+		if node.IsIngressGateway {
+			fwUpdate.IsIngressGw = true
+			_, extPeerIDAndAddrs, err = GetExtPeers(&node)
+			if err == nil {
+				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
+					extPeerIdAndAddr := extPeerIdAndAddr
+					nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
+						PeerAddr: net.IPNet{
+							IP:   net.ParseIP(extPeerIdAndAddr.Address),
+							Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
+						},
+						PeerKey: extPeerIdAndAddr.ID,
+						Allow:   true,
+						ID:      extPeerIdAndAddr.ID,
+					}
+				}
+				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
+					extPeerIdAndAddr := extPeerIdAndAddr
+
+					fwUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
+						Masquerade: true,
+						IngGwAddr: net.IPNet{
+							IP:   net.ParseIP(node.PrimaryAddress()),
+							Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
+						},
+						Network: node.PrimaryNetworkRange(),
+						ExtPeerAddr: net.IPNet{
+							IP:   net.ParseIP(extPeerIdAndAddr.Address),
+							Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
+						},
+						ExtPeerKey: extPeerIdAndAddr.ID,
+						Peers:      filterNodeMapForClientACLs(extPeerIdAndAddr.ID, node.Network, nodePeerMap),
+					}
+				}
+			} else if !database.IsEmptyRecord(err) {
+				logger.Log(1, "error retrieving external clients:", err.Error())
+			}
+		}
+		if node.IsEgressGateway {
+			fwUpdate.IsEgressGw = true
+			fwUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
+				EgressID: node.ID.String(),
+				Network:  node.PrimaryNetworkRange(),
+				EgressGwAddr: net.IPNet{
+					IP:   net.ParseIP(node.PrimaryAddress()),
+					Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
+				},
+				GwPeers:     nodePeerMap,
+				EgressGWCfg: node.EgressGatewayRequest,
+			}
+		}
+	}
+	return fwUpdate, nil
+}
+
 // GetPeerListenPort - given a host, retrieve it's appropriate listening port
 // GetPeerListenPort - given a host, retrieve it's appropriate listening port
 func GetPeerListenPort(host *models.Host) int {
 func GetPeerListenPort(host *models.Host) int {
 	peerPort := host.ListenPort
 	peerPort := host.ListenPort
@@ -463,7 +474,7 @@ func GetProxyListenPort(host *models.Host) int {
 	return proxyPort
 	return proxyPort
 }
 }
 
 
-func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
+func GetExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
 	var peers []wgtypes.PeerConfig
 	var peers []wgtypes.PeerConfig
 	var idsAndAddr []models.IDandAddr
 	var idsAndAddr []models.IDandAddr
 	extPeers, err := GetNetworkExtClients(node.Network)
 	extPeers, err := GetNetworkExtClients(node.Network)
@@ -598,7 +609,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 
 
 	// handle ingress gateway peers
 	// handle ingress gateway peers
 	if peer.IsIngressGateway {
 	if peer.IsIngressGateway {
-		extPeers, _, err := getExtPeers(peer)
+		extPeers, _, err := GetExtPeers(peer)
 		if err != nil {
 		if err != nil {
 			logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
 			logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
 		}
 		}
@@ -630,42 +641,36 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 	return allowedips
 	return allowedips
 }
 }
 
 
-func getEgressIPs(node, peer *models.Node) []net.IPNet {
-	host, err := GetHost(node.HostID.String())
-	if err != nil {
-		logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
-	}
-	peerHost, err := GetHost(peer.HostID.String())
-	if err != nil {
-		logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error())
-	}
+// getEgressIPs - gets the egress IPs for a client
+func getEgressIPs(client *models.Client) []net.IPNet {
 
 
 	//check for internet gateway
 	//check for internet gateway
 	internetGateway := false
 	internetGateway := false
-	if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") {
+	if slices.Contains(client.Node.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(client.Node.EgressGatewayRanges, "::/0") {
 		internetGateway = true
 		internetGateway = true
 	}
 	}
 	allowedips := []net.IPNet{}
 	allowedips := []net.IPNet{}
-	for _, iprange := range peer.EgressGatewayRanges { // go through each cidr for egress gateway
-		_, ipnet, err := net.ParseCIDR(iprange) // confirming it's valid cidr
+	for _, iprange := range client.Node.EgressGatewayRanges { // go through each cidr for egress gateway
+		ip, cidr, err := net.ParseCIDR(iprange) // confirming it's valid cidr
 		if err != nil {
 		if err != nil {
 			logger.Log(1, "could not parse gateway IP range. Not adding ", iprange)
 			logger.Log(1, "could not parse gateway IP range. Not adding ", iprange)
 			continue // if can't parse CIDR
 			continue // if can't parse CIDR
 		}
 		}
+		cidr.IP = ip
 		// getting the public ip of node
 		// getting the public ip of node
-		if ipnet.Contains(peerHost.EndpointIP) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node
-			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", host.EndpointIP.String(), ", omitting")
+		if cidr.Contains(client.Host.EndpointIP) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node
+			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", client.Host.EndpointIP.String(), ", omitting")
 			continue // skip adding egress range if overlaps with node's ip
 			continue // skip adding egress range if overlaps with node's ip
 		}
 		}
 		// TODO: Could put in a lot of great logic to avoid conflicts / bad routes
 		// TODO: Could put in a lot of great logic to avoid conflicts / bad routes
-		if ipnet.Contains(node.LocalAddress.IP) && !internetGateway { // ensuring egress gateway range does not contain public ip of node
-			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.LocalAddress.String(), ", omitting")
+		if cidr.Contains(client.Node.LocalAddress.IP) && !internetGateway { // ensuring egress gateway range does not contain public ip of node
+			logger.Log(2, "egress IP range of ", iprange, " overlaps with ", client.Node.LocalAddress.String(), ", omitting")
 			continue // skip adding egress range if overlaps with node's local ip
 			continue // skip adding egress range if overlaps with node's local ip
 		}
 		}
 		if err != nil {
 		if err != nil {
 			logger.Log(1, "error encountered when setting egress range", err.Error())
 			logger.Log(1, "error encountered when setting egress range", err.Error())
 		} else {
 		} else {
-			allowedips = append(allowedips, *ipnet)
+			allowedips = append(allowedips, *cidr)
 		}
 		}
 	}
 	}
 	return allowedips
 	return allowedips
@@ -690,8 +695,21 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 	// handle egress gateway peers
 	// handle egress gateway peers
 	if peer.IsEgressGateway {
 	if peer.IsEgressGateway {
 		//hasGateway = true
 		//hasGateway = true
-		egressIPs := getEgressIPs(node, peer)
-		allowedips = append(allowedips, egressIPs...)
+		host, err := GetHost(peer.HostID.String())
+		if err == nil {
+			egressIPs := getEgressIPs(
+				&models.Client{
+					Host: *host,
+					Node: *peer,
+				})
+			allowedips = append(allowedips, egressIPs...)
+		}
+	}
+	if peer.IsRelay {
+		for _, relayed := range peer.RelayedNodes {
+			allowed := getRelayedAddresses(relayed)
+			allowedips = append(allowedips, allowed...)
+		}
 	}
 	}
 	return allowedips
 	return allowedips
 }
 }
@@ -733,3 +751,190 @@ func filterNodeMapForClientACLs(publicKey, network string, nodePeerMap map[strin
 	}
 	}
 	return nodePeerMap
 	return nodePeerMap
 }
 }
+
+func GetPeerUpdate(host *models.Host) []wgtypes.PeerConfig {
+	peerUpdate := []wgtypes.PeerConfig{}
+	for _, nodeStr := range host.Nodes {
+		node, err := GetNodeByID(nodeStr)
+		if err != nil {
+			continue
+		}
+		client := models.Client{Host: *host, Node: node}
+		peers, err := GetNetworkClients(node.Network)
+		if err != nil {
+			continue
+		}
+		if node.IsRelayed {
+			peerUpdate = append(peerUpdate, peerUpdateForRelayed(&client, peers)...)
+			continue
+		}
+		if node.IsRelay {
+			peerUpdate = append(peerUpdate, peerUpdateForRelay(&client, peers)...)
+			continue
+		}
+		for _, peer := range peers {
+			if peer.Host.ID == client.Host.ID {
+				continue
+			}
+			// if peer is relayed by some other node, remove it from the peer list,  it
+			// will be added to allowedips of relay peer
+			if peer.Node.IsRelayed {
+				update := wgtypes.PeerConfig{
+					PublicKey: peer.Host.PublicKey,
+					Remove:    true,
+				}
+				peerUpdate = append(peerUpdate, update)
+				continue
+			}
+			update := wgtypes.PeerConfig{
+				PublicKey:         peer.Host.PublicKey,
+				ReplaceAllowedIPs: true,
+				Endpoint: &net.UDPAddr{
+					IP:   peer.Host.EndpointIP,
+					Port: peer.Host.ListenPort,
+				},
+				PersistentKeepaliveInterval: &peer.Node.PersistentKeepalive,
+			}
+			// if peer is a relay that relays us, don't do anything
+			if peer.Node.IsRelay && client.Node.RelayedBy == peer.Node.ID.String() {
+				continue
+			} else {
+				update.AllowedIPs = append(update.AllowedIPs, getRelayAllowedIPs(&peer)...)
+			}
+			//normal peer
+			update.AllowedIPs = append(update.AllowedIPs, AddAllowedIPs(&peer)...)
+			peerUpdate = append(peerUpdate, update)
+		}
+	}
+	return peerUpdate
+}
+
+func AddAllowedIPs(peer *models.Client) []net.IPNet {
+	allowedIPs := []net.IPNet{}
+	if peer.Node.Address.IP != nil {
+		peer.Node.Address.Mask = net.CIDRMask(32, 32)
+		allowedIPs = append(allowedIPs, peer.Node.Address)
+	}
+	if peer.Node.Address6.IP != nil {
+		peer.Node.Address6.Mask = net.CIDRMask(128, 128)
+		allowedIPs = append(allowedIPs, peer.Node.Address6)
+	}
+	if peer.Node.IsEgressGateway {
+		allowedIPs = append(allowedIPs, getEgressIPs(peer)...)
+	}
+	if peer.Node.IsIngressGateway {
+		allowedIPs = append(allowedIPs, getIngressIPs(peer)...)
+	}
+	return allowedIPs
+}
+
+// getRelayAllowedIPs returns the list of allowedips for a peer that is a relay
+func getRelayAllowedIPs(peer *models.Client) []net.IPNet {
+	var relayIPs []net.IPNet
+	if !peer.Node.IsRelay {
+		logger.Log(0, "getRelayAllowedIPs called for a non-relay node", peer.Host.Name)
+		return relayIPs
+	}
+	//if !client.Node.IsRelayed || client.Node.RelayedBy != peer.Node.ID.String() {
+	//logger.Log(0, "getRelayAllowedIPs called for non-relayed node", client.Host.Name, peer.Host.Name)
+	//return relayIPs
+	//}
+	for _, relayed := range peer.Node.RelayedNodes {
+		relayedNode, err := GetNodeByID(relayed)
+		if err != nil {
+			logger.Log(0, "retrieve relayed node", err.Error())
+			continue
+		}
+		if relayedNode.Address.IP != nil {
+			relayedNode.Address.Mask = net.CIDRMask(32, 32)
+			relayIPs = append(relayIPs, relayedNode.Address)
+		}
+		if relayedNode.Address6.IP != nil {
+			relayedNode.Address.Mask = net.CIDRMask(128, 128)
+			relayIPs = append(relayIPs, relayedNode.Address6)
+		}
+		host, err := GetHost(relayedNode.HostID.String())
+		if err == nil {
+			if relayedNode.IsRelay {
+				relayIPs = append(relayIPs, getRelayAllowedIPs(
+					&models.Client{
+						Host: *host,
+						Node: relayedNode,
+					})...)
+			}
+			if relayedNode.IsEgressGateway {
+				relayIPs = append(relayIPs, getEgressIPs(
+					&models.Client{
+						Host: *host,
+						Node: relayedNode,
+					})...)
+			}
+			if relayedNode.IsIngressGateway {
+				relayIPs = append(relayIPs, getIngressIPs(
+					&models.Client{
+						Host: *host,
+						Node: relayedNode,
+					})...)
+			}
+		}
+	}
+	return relayIPs
+}
+
+// getIngressIPs returns the additional allowedips (ext client addresses) that need
+// to be included for an ingress gateway peer
+// TODO:  add ExtraAllowedIPs
+func getIngressIPs(peer *models.Client) []net.IPNet {
+	var ingressIPs []net.IPNet
+	extclients, err := GetNetworkExtClients(peer.Node.Network)
+	if err != nil {
+		return ingressIPs
+	}
+	for _, ec := range extclients {
+		if ec.IngressGatewayID == peer.Node.ID.String() {
+			if ec.Address != "" {
+				ip, cidr, err := net.ParseCIDR(ec.Address)
+				if err != nil {
+					continue
+				}
+				cidr.IP = ip
+				ingressIPs = append(ingressIPs, *cidr)
+			}
+			if ec.Address6 != "" {
+				ip, cidr, err := net.ParseCIDR(ec.Address6)
+				if err != nil {
+					continue
+				}
+				cidr.IP = ip
+				ingressIPs = append(ingressIPs, *cidr)
+			}
+		}
+	}
+	return ingressIPs
+}
+
+// GetPeerUpdateForRelay - returns the peer update for a relay node
+func GetPeerUpdateForRelay(client *models.Client, peers []models.Client) []wgtypes.PeerConfig {
+	peerConfig := []wgtypes.PeerConfig{}
+	if !client.Node.IsRelay {
+		return []wgtypes.PeerConfig{}
+	}
+	for _, peer := range peers {
+		if peer.Host.ID == client.Host.ID {
+			continue
+		}
+		update := wgtypes.PeerConfig{
+			PublicKey:         peer.Host.PublicKey,
+			ReplaceAllowedIPs: true,
+			Remove:            false,
+			Endpoint: &net.UDPAddr{
+				IP:   peer.Host.EndpointIP,
+				Port: peer.Host.ListenPort,
+			},
+			PersistentKeepaliveInterval: &peer.Node.PersistentKeepalive,
+		}
+		update.AllowedIPs = append(update.AllowedIPs, AddAllowedIPs(&peer)...)
+		peerConfig = append(peerConfig, update)
+	}
+	return peerConfig
+}

+ 183 - 156
logic/relay.go

@@ -4,227 +4,254 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"time"
+	"net"
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
 
 
 // CreateRelay - creates a relay
 // CreateRelay - creates a relay
-func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) {
-	var returnnodes []models.Node
-
+func CreateRelay(relay models.RelayRequest) ([]models.Client, models.Node, error) {
+	var relayedClients []models.Client
 	node, err := GetNodeByID(relay.NodeID)
 	node, err := GetNodeByID(relay.NodeID)
 	if err != nil {
 	if err != nil {
-		return returnnodes, models.Node{}, err
+		return relayedClients, models.Node{}, err
 	}
 	}
 	host, err := GetHost(node.HostID.String())
 	host, err := GetHost(node.HostID.String())
 	if err != nil {
 	if err != nil {
-		return returnnodes, models.Node{}, err
+		return relayedClients, models.Node{}, err
 	}
 	}
 	if host.OS != "linux" {
 	if host.OS != "linux" {
-		return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
+		return relayedClients, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
 	}
 	}
 	err = ValidateRelay(relay)
 	err = ValidateRelay(relay)
 	if err != nil {
 	if err != nil {
-		return returnnodes, models.Node{}, err
+		return relayedClients, models.Node{}, err
 	}
 	}
 	node.IsRelay = true
 	node.IsRelay = true
-	node.RelayAddrs = relay.RelayAddrs
-
+	node.RelayedNodes = relay.RelayedNodes
 	node.SetLastModified()
 	node.SetLastModified()
 	nodeData, err := json.Marshal(&node)
 	nodeData, err := json.Marshal(&node)
 	if err != nil {
 	if err != nil {
-		return returnnodes, node, err
+		return relayedClients, node, err
 	}
 	}
 	if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
 	if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs)
-	if err != nil {
-		return returnnodes, node, err
-	}
-	return returnnodes, node, nil
-}
-
-// CreateHostRelay - creates a host relay
-func CreateHostRelay(relay models.HostRelayRequest) (relayHost *models.Host, relayedHosts []models.Host, err error) {
-
-	relayHost, err = GetHost(relay.HostID)
-	if err != nil {
-		return
+		return relayedClients, models.Node{}, err
 	}
 	}
-	err = validateHostRelay(relay)
-	if err != nil {
-		return
-	}
-	relayHost.IsRelay = true
-	relayHost.ProxyEnabled = true
-	relayHost.RelayedHosts = relay.RelayedHosts
-	err = UpsertHost(relayHost)
-	if err != nil {
-		return
-	}
-	relayedHosts = SetRelayedHosts(true, relay.HostID, relay.RelayedHosts)
-	return
-}
-
-// SetRelayedHosts - updates the relayed hosts status
-func SetRelayedHosts(setRelayed bool, relayHostID string, relayedHostIDs []string) []models.Host {
-	var relayedHosts []models.Host
-	for _, relayedHostID := range relayedHostIDs {
-		host, err := GetHost(relayedHostID)
-		if err == nil {
-			if setRelayed {
-				host.IsRelayed = true
-				host.RelayedBy = relayHostID
-				host.ProxyEnabled = true
-			} else {
-				host.IsRelayed = false
-				host.RelayedBy = ""
-			}
-			err = UpsertHost(host)
-			if err == nil {
-				relayedHosts = append(relayedHosts, *host)
-			}
+	relayedClients = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes)
+	for _, relayed := range relayedClients {
+		data, err := json.Marshal(&relayed.Node)
+		if err != nil {
+			logger.Log(0, "marshalling relayed node", err.Error())
+			continue
+		}
+		if err := database.Insert(relayed.Node.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil {
+			logger.Log(0, "inserting relayed node", err.Error())
+			continue
 		}
 		}
 	}
 	}
-	return relayedHosts
+	return relayedClients, node, nil
 }
 }
 
 
-// SetRelayedNodes- set relayed nodes
-func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]models.Node, error) {
-	var returnnodes []models.Node
-	networkNodes, err := GetNetworkNodes(networkName)
-	if err != nil {
-		return returnnodes, err
-	}
-	for _, node := range networkNodes {
-		for _, addr := range addrs {
-			if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
-				if setRelayed {
-					node.IsRelayed = true
-				} else {
-					node.IsRelayed = false
-				}
-				data, err := json.Marshal(&node)
-				if err != nil {
-					return returnnodes, err
-				}
-				database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
-				returnnodes = append(returnnodes, node)
-			}
+// SetRelayedNodes- sets and saves node as relayed
+func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Client {
+	var returnnodes []models.Client
+	for _, id := range relayed {
+		node, err := GetNodeByID(id)
+		if err != nil {
+			logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error())
+			continue
 		}
 		}
-	}
-	return returnnodes, nil
-}
-func GetRelayedNodes(relayNode *models.Node) ([]models.Node, error) {
-	var returnnodes []models.Node
-	networkNodes, err := GetNetworkNodes(relayNode.Network)
-	if err != nil {
-		return returnnodes, err
-	}
-	for _, node := range networkNodes {
-		for _, addr := range relayNode.RelayAddrs {
-			if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
-				returnnodes = append(returnnodes, node)
-			}
+		node.IsRelayed = setRelayed
+		if node.IsRelayed {
+			node.RelayedBy = relay
+		} else {
+			node.RelayedBy = ""
 		}
 		}
-	}
-	return returnnodes, nil
-}
-
-// GetRelayedHosts - gets the relayed hosts of a relay host
-func GetRelayedHosts(relayHost *models.Host) []models.Host {
-	relayedHosts := []models.Host{}
-
-	for _, hostID := range relayHost.RelayedHosts {
-		relayedHost, err := GetHost(hostID)
+		node.SetLastModified()
+		data, err := json.Marshal(&node)
+		if err != nil {
+			logger.Log(0, "setRelayedNodes.Marshal", err.Error())
+			continue
+		}
+		if err := database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil {
+			logger.Log(0, "setRelayedNodes.Insert", err.Error())
+			continue
+		}
+		host, err := GetHost(node.HostID.String())
 		if err == nil {
 		if err == nil {
-			relayedHosts = append(relayedHosts, *relayedHost)
+			returnnodes = append(returnnodes, models.Client{
+				Host: *host,
+				Node: node,
+			})
 		}
 		}
 	}
 	}
-	return relayedHosts
+	return returnnodes
 }
 }
 
 
 // ValidateRelay - checks if relay is valid
 // ValidateRelay - checks if relay is valid
 func ValidateRelay(relay models.RelayRequest) error {
 func ValidateRelay(relay models.RelayRequest) error {
 	var err error
 	var err error
 	//isIp := functions.IsIpCIDR(gateway.RangeString)
 	//isIp := functions.IsIpCIDR(gateway.RangeString)
-	empty := len(relay.RelayAddrs) == 0
+	empty := len(relay.RelayedNodes) == 0
 	if empty {
 	if empty {
-		err = errors.New("IP Ranges Cannot Be Empty")
+		err = errors.New("relayed nodes cannot be empty")
 	}
 	}
 	return err
 	return err
 }
 }
 
 
-func validateHostRelay(relay models.HostRelayRequest) error {
-	if len(relay.RelayedHosts) == 0 {
-		return errors.New("relayed hosts are empty")
-	}
-	return nil
-}
-
-// UpdateRelay - updates a relay
-func UpdateRelay(network string, oldAddrs []string, newAddrs []string) []models.Node {
-	var returnnodes []models.Node
-	time.Sleep(time.Second / 4)
-	_, err := SetRelayedNodes(false, network, oldAddrs)
-	if err != nil {
-		logger.Log(1, err.Error())
-	}
-	returnnodes, err = SetRelayedNodes(true, network, newAddrs)
-	if err != nil {
-		logger.Log(1, err.Error())
-	}
-	return returnnodes
+// UpdateRelayed - updates relay nodes
+func UpdateRelayed(relay string, oldNodes []string, newNodes []string) []models.Client {
+	_ = SetRelayedNodes(false, relay, oldNodes)
+	return SetRelayedNodes(true, relay, newNodes)
 }
 }
 
 
 // DeleteRelay - deletes a relay
 // DeleteRelay - deletes a relay
-func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
-	var returnnodes []models.Node
+func DeleteRelay(network, nodeid string) ([]models.Client, models.Node, error) {
+	var returnClients []models.Client
 	node, err := GetNodeByID(nodeid)
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
 	if err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	returnnodes, err = SetRelayedNodes(false, node.Network, node.RelayAddrs)
-	if err != nil {
-		return returnnodes, node, err
+		return returnClients, models.Node{}, err
 	}
 	}
 
 
+	returnClients = SetRelayedNodes(false, nodeid, node.RelayedNodes)
 	node.IsRelay = false
 	node.IsRelay = false
-	node.RelayAddrs = []string{}
+	node.RelayedNodes = []string{}
 	node.SetLastModified()
 	node.SetLastModified()
-
 	data, err := json.Marshal(&node)
 	data, err := json.Marshal(&node)
 	if err != nil {
 	if err != nil {
-		return returnnodes, models.Node{}, err
+		return returnClients, models.Node{}, err
 	}
 	}
 	if err = database.Insert(nodeid, string(data), database.NODES_TABLE_NAME); err != nil {
 	if err = database.Insert(nodeid, string(data), database.NODES_TABLE_NAME); err != nil {
-		return returnnodes, models.Node{}, err
+		return returnClients, models.Node{}, err
 	}
 	}
-	return returnnodes, node, nil
+	return returnClients, node, nil
 }
 }
 
 
-// DeleteHostRelay - removes host as relay
-func DeleteHostRelay(relayHostID string) (relayHost *models.Host, relayedHosts []models.Host, err error) {
-	relayHost, err = GetHost(relayHostID)
+func getRelayedAddresses(id string) []net.IPNet {
+	addrs := []net.IPNet{}
+	node, err := GetNodeByID(id)
 	if err != nil {
 	if err != nil {
-		return
+		logger.Log(0, "getRelayedAddresses: "+err.Error())
+		return addrs
+	}
+	if node.Address.IP != nil {
+		node.Address.Mask = net.CIDRMask(32, 32)
+		addrs = append(addrs, node.Address)
+	}
+	if node.Address6.IP != nil {
+		node.Address6.Mask = net.CIDRMask(128, 128)
+		addrs = append(addrs, node.Address6)
+	}
+	return addrs
+}
+
+// peerUpdateForRelayed - returns the peerConfig for a relayed node
+func peerUpdateForRelayed(client *models.Client, peers []models.Client) []wgtypes.PeerConfig {
+	peerConfig := []wgtypes.PeerConfig{}
+	if !client.Node.IsRelayed {
+		logger.Log(0, "GetPeerUpdateForRelayed called for non-relayed node ", client.Host.Name)
+		return []wgtypes.PeerConfig{}
+	}
+	relayNode, err := GetNodeByID(client.Node.RelayedBy)
+	if err != nil {
+		logger.Log(0, "error retrieving relay node", err.Error())
+		return []wgtypes.PeerConfig{}
+	}
+	host, err := GetHost(relayNode.HostID.String())
+	if err != nil {
+		return []wgtypes.PeerConfig{}
+	}
+	relay := models.Client{
+		Host: *host,
+		Node: relayNode,
+	}
+	for _, peer := range peers {
+		if peer.Host.ID == client.Host.ID {
+			continue
+		}
+		if peer.Host.ID == relay.Host.ID { // add relay as a peer
+			update := peerUpdateForRelayedByRelay(client, &relay)
+			peerConfig = append(peerConfig, update)
+			continue
+		}
+		update := wgtypes.PeerConfig{
+			PublicKey: peer.Host.PublicKey,
+			Remove:    true,
+		}
+		peerConfig = append(peerConfig, update)
+	}
+	return peerConfig
+}
+
+// peerUpdateForRelayedByRelay - returns the peerConfig for a node relayed by relay
+func peerUpdateForRelayedByRelay(relayed, relay *models.Client) wgtypes.PeerConfig {
+	if relayed.Node.RelayedBy != relay.Node.ID.String() {
+		logger.Log(0, "peerUpdateForRelayedByRelay called with invalid parameters")
+		return wgtypes.PeerConfig{}
+	}
+	update := wgtypes.PeerConfig{
+		PublicKey:         relay.Host.PublicKey,
+		ReplaceAllowedIPs: true,
+		Endpoint: &net.UDPAddr{
+			IP:   relay.Host.EndpointIP,
+			Port: relay.Host.ListenPort,
+		},
+		PersistentKeepaliveInterval: &relay.Node.PersistentKeepalive,
+	}
+	if relay.Node.Address.IP != nil {
+		relay.Node.Address.Mask = net.CIDRMask(32, 32)
+		update.AllowedIPs = append(update.AllowedIPs, relay.Node.Address)
+	}
+	if relay.Node.Address6.IP != nil {
+		relay.Node.Address6.Mask = net.CIDRMask(128, 128)
+		update.AllowedIPs = append(update.AllowedIPs, relay.Node.Address6)
 	}
 	}
-	relayedHosts = SetRelayedHosts(false, relayHostID, relayHost.RelayedHosts)
-	relayHost.IsRelay = false
-	relayHost.RelayedHosts = []string{}
-	err = UpsertHost(relayHost)
+	if relay.Node.IsEgressGateway {
+		update.AllowedIPs = append(update.AllowedIPs, getEgressIPs(relay)...)
+	}
+	if relay.Node.IsIngressGateway {
+		update.AllowedIPs = append(update.AllowedIPs, getIngressIPs(relay)...)
+	}
+	peers, err := GetNetworkClients(relay.Node.Network)
 	if err != nil {
 	if err != nil {
-		return
+		logger.Log(0, "error getting network clients", err.Error())
+		return update
+	}
+	for _, peer := range peers {
+		if peer.Host.ID == relayed.Host.ID || peer.Host.ID == relay.Host.ID {
+			continue
+		}
+		update.AllowedIPs = append(update.AllowedIPs, AddAllowedIPs(&peer)...)
 	}
 	}
-	return
+	return update
 }
 }
 
 
-// UpdateHostRelay - updates the relay host with new relayed hosts
-func UpdateHostRelay(relayHostID string, oldRelayedHosts, newRelayedHosts []string) {
-	_ = SetRelayedHosts(false, relayHostID, oldRelayedHosts)
-	_ = SetRelayedHosts(true, relayHostID, newRelayedHosts)
+// peerUpdateForRelay - returns the peerConfig for a relay
+func peerUpdateForRelay(relay *models.Client, peers []models.Client) []wgtypes.PeerConfig {
+	peerConfig := []wgtypes.PeerConfig{}
+	if !relay.Node.IsRelay {
+		logger.Log(0, "GetPeerUpdateForRelay called for non-relay node ", relay.Host.Name)
+		return []wgtypes.PeerConfig{}
+	}
+	for _, peer := range peers {
+		if peer.Host.ID == relay.Host.ID {
+			continue
+		}
+		update := wgtypes.PeerConfig{
+			PublicKey:         peer.Host.PublicKey,
+			ReplaceAllowedIPs: true,
+			Remove:            false,
+			Endpoint: &net.UDPAddr{
+				IP:   peer.Host.EndpointIP,
+				Port: peer.Host.ListenPort,
+			},
+			PersistentKeepaliveInterval: &peer.Node.PersistentKeepalive,
+		}
+		update.AllowedIPs = append(update.AllowedIPs, AddAllowedIPs(&peer)...)
+		peerConfig = append(peerConfig, update)
+	}
+	return peerConfig
 }
 }

+ 3 - 3
logic/wireguard.go

@@ -29,11 +29,11 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 		}
 		}
 	}
 	}
 	if newNode.IsRelay {
 	if newNode.IsRelay {
-		if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) {
+		if len(currentNode.RelayedNodes) != len(newNode.RelayedNodes) {
 			return true
 			return true
 		}
 		}
-		for _, address := range newNode.RelayAddrs {
-			if !StringSliceContains(currentNode.RelayAddrs, address) {
+		for _, node := range newNode.RelayedNodes {
+			if !StringSliceContains(currentNode.RelayedNodes, node) {
 				return true
 				return true
 			}
 			}
 		}
 		}

+ 0 - 12
models/api_host.go

@@ -30,10 +30,6 @@ type ApiHost struct {
 	Nodes              []string `json:"nodes"`
 	Nodes              []string `json:"nodes"`
 	ProxyEnabled       bool     `json:"proxy_enabled" yaml:"proxy_enabled"`
 	ProxyEnabled       bool     `json:"proxy_enabled" yaml:"proxy_enabled"`
 	IsDefault          bool     `json:"isdefault" yaml:"isdefault"`
 	IsDefault          bool     `json:"isdefault" yaml:"isdefault"`
-	IsRelayed          bool     `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
-	RelayedBy          string   `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"`
-	IsRelay            bool     `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
-	RelayedHosts       []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"`
 }
 }
 
 
 // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
 // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
@@ -67,10 +63,6 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
 	a.Verbosity = h.Verbosity
 	a.Verbosity = h.Verbosity
 	a.Version = h.Version
 	a.Version = h.Version
 	a.IsDefault = h.IsDefault
 	a.IsDefault = h.IsDefault
-	a.IsRelay = h.IsRelay
-	a.RelayedHosts = h.RelayedHosts
-	a.IsRelayed = h.IsRelayed
-	a.RelayedBy = h.RelayedBy
 	return &a
 	return &a
 }
 }
 
 
@@ -108,10 +100,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
 	h.Nodes = currentHost.Nodes
 	h.Nodes = currentHost.Nodes
 	h.TrafficKeyPublic = currentHost.TrafficKeyPublic
 	h.TrafficKeyPublic = currentHost.TrafficKeyPublic
 	h.OS = currentHost.OS
 	h.OS = currentHost.OS
-	h.RelayedBy = a.RelayedBy
-	h.RelayedHosts = a.RelayedHosts
-	h.IsRelay = a.IsRelay
-	h.IsRelayed = a.IsRelayed
 	h.ProxyEnabled = a.ProxyEnabled
 	h.ProxyEnabled = a.ProxyEnabled
 	h.IsDefault = a.IsDefault
 	h.IsDefault = a.IsDefault
 	h.NatType = currentHost.NatType
 	h.NatType = currentHost.NatType

+ 7 - 3
models/api_node.go

@@ -25,11 +25,12 @@ type ApiNode struct {
 	NetworkRange6           string   `json:"networkrange6"`
 	NetworkRange6           string   `json:"networkrange6"`
 	IsRelayed               bool     `json:"isrelayed"`
 	IsRelayed               bool     `json:"isrelayed"`
 	IsRelay                 bool     `json:"isrelay"`
 	IsRelay                 bool     `json:"isrelay"`
+	RelayedBy               string   `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
+	RelayedNodes            []string `json:"relaynodes" yaml:"relayedNodes"`
 	IsEgressGateway         bool     `json:"isegressgateway"`
 	IsEgressGateway         bool     `json:"isegressgateway"`
 	IsIngressGateway        bool     `json:"isingressgateway"`
 	IsIngressGateway        bool     `json:"isingressgateway"`
 	EgressGatewayRanges     []string `json:"egressgatewayranges"`
 	EgressGatewayRanges     []string `json:"egressgatewayranges"`
 	EgressGatewayNatEnabled bool     `json:"egressgatewaynatenabled"`
 	EgressGatewayNatEnabled bool     `json:"egressgatewaynatenabled"`
-	RelayAddrs              []string `json:"relayaddrs"`
 	FailoverNode            string   `json:"failovernode"`
 	FailoverNode            string   `json:"failovernode"`
 	DNSOn                   bool     `json:"dnson"`
 	DNSOn                   bool     `json:"dnson"`
 	IngressDns              string   `json:"ingressdns"`
 	IngressDns              string   `json:"ingressdns"`
@@ -53,6 +54,8 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	convertedNode.HostID, _ = uuid.Parse(a.HostID)
 	convertedNode.HostID, _ = uuid.Parse(a.HostID)
 	convertedNode.IsRelay = a.IsRelay
 	convertedNode.IsRelay = a.IsRelay
 	convertedNode.IsRelayed = a.IsRelayed
 	convertedNode.IsRelayed = a.IsRelayed
+	convertedNode.RelayedBy = a.RelayedBy
+	convertedNode.RelayedNodes = a.RelayedNodes
 	convertedNode.PendingDelete = a.PendingDelete
 	convertedNode.PendingDelete = a.PendingDelete
 	convertedNode.Failover = a.Failover
 	convertedNode.Failover = a.Failover
 	convertedNode.IsEgressGateway = a.IsEgressGateway
 	convertedNode.IsEgressGateway = a.IsEgressGateway
@@ -66,7 +69,7 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
 	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
 	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
 	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
 	convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive)
 	convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive)
-	convertedNode.RelayAddrs = a.RelayAddrs
+	convertedNode.RelayedNodes = a.RelayedNodes
 	convertedNode.DefaultACL = a.DefaultACL
 	convertedNode.DefaultACL = a.DefaultACL
 	convertedNode.OwnerID = currentNode.OwnerID
 	convertedNode.OwnerID = currentNode.OwnerID
 	_, networkRange, err := net.ParseCIDR(a.NetworkRange)
 	_, networkRange, err := net.ParseCIDR(a.NetworkRange)
@@ -140,11 +143,12 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
 	}
 	}
 	apiNode.IsRelayed = nm.IsRelayed
 	apiNode.IsRelayed = nm.IsRelayed
 	apiNode.IsRelay = nm.IsRelay
 	apiNode.IsRelay = nm.IsRelay
+	apiNode.RelayedBy = nm.RelayedBy
+	apiNode.RelayedNodes = nm.RelayedNodes
 	apiNode.IsEgressGateway = nm.IsEgressGateway
 	apiNode.IsEgressGateway = nm.IsEgressGateway
 	apiNode.IsIngressGateway = nm.IsIngressGateway
 	apiNode.IsIngressGateway = nm.IsIngressGateway
 	apiNode.EgressGatewayRanges = nm.EgressGatewayRanges
 	apiNode.EgressGatewayRanges = nm.EgressGatewayRanges
 	apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled
 	apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled
-	apiNode.RelayAddrs = nm.RelayAddrs
 	apiNode.FailoverNode = nm.FailoverNode.String()
 	apiNode.FailoverNode = nm.FailoverNode.String()
 	if isUUIDSet(apiNode.FailoverNode) {
 	if isUUIDSet(apiNode.FailoverNode) {
 		apiNode.FailoverNode = ""
 		apiNode.FailoverNode = ""

+ 6 - 4
models/host.go

@@ -64,10 +64,6 @@ type Host struct {
 	TrafficKeyPublic   []byte           `json:"traffickeypublic" yaml:"traffickeypublic"`
 	TrafficKeyPublic   []byte           `json:"traffickeypublic" yaml:"traffickeypublic"`
 	InternetGateway    net.UDPAddr      `json:"internetgateway" yaml:"internetgateway"`
 	InternetGateway    net.UDPAddr      `json:"internetgateway" yaml:"internetgateway"`
 	Nodes              []string         `json:"nodes" yaml:"nodes"`
 	Nodes              []string         `json:"nodes" yaml:"nodes"`
-	IsRelayed          bool             `json:"isrelayed" yaml:"isrelayed"`
-	RelayedBy          string           `json:"relayed_by" yaml:"relayed_by"`
-	IsRelay            bool             `json:"isrelay" yaml:"isrelay"`
-	RelayedHosts       []string         `json:"relay_hosts" yaml:"relay_hosts"`
 	Interfaces         []Iface          `json:"interfaces" yaml:"interfaces"`
 	Interfaces         []Iface          `json:"interfaces" yaml:"interfaces"`
 	DefaultInterface   string           `json:"defaultinterface" yaml:"defaultinterface"`
 	DefaultInterface   string           `json:"defaultinterface" yaml:"defaultinterface"`
 	EndpointIP         net.IP           `json:"endpointip" yaml:"endpointip"`
 	EndpointIP         net.IP           `json:"endpointip" yaml:"endpointip"`
@@ -81,6 +77,12 @@ type Host struct {
 	TurnEndpoint       *netip.AddrPort  `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
 	TurnEndpoint       *netip.AddrPort  `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
 }
 }
 
 
+// Client - represents a client on the network
+type Client struct {
+	Host Host `json:"host" yaml:"host"`
+	Node Node `json:"node" yaml:"node"`
+}
+
 // FormatBool converts a boolean to a [yes|no] string
 // FormatBool converts a boolean to a [yes|no] string
 func FormatBool(b bool) string {
 func FormatBool(b bool) string {
 	s := "no"
 	s := "no"

+ 30 - 6
models/mqtt.go

@@ -14,12 +14,10 @@ type HostPeerUpdate struct {
 	ServerAddrs     []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
 	ServerAddrs     []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
 	NodePeers       []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
 	NodePeers       []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
 	Peers           []wgtypes.PeerConfig
 	Peers           []wgtypes.PeerConfig
-	HostPeerIDs     HostPeerMap           `json:"hostpeerids" bson:"hostpeerids" yaml:"hostpeerids"`
-	ProxyUpdate     ProxyManagerPayload   `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
-	EgressInfo      map[string]EgressInfo `json:"egress_info" bson:"egress_info" yaml:"egress_info"` // map key is node ID
-	IngressInfo     IngressInfo           `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"`
-	PeerIDs         PeerMap               `json:"peerids" bson:"peerids" yaml:"peerids"`
-	HostNetworkInfo HostInfoMap           `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
+	HostPeerIDs     HostPeerMap         `json:"hostpeerids" bson:"hostpeerids" yaml:"hostpeerids"`
+	ProxyUpdate     ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
+	PeerIDs         PeerMap             `json:"peerids" bson:"peerids" yaml:"peerids"`
+	HostNetworkInfo HostInfoMap         `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
 }
 }
 
 
 // IngressInfo - struct for ingress info
 // IngressInfo - struct for ingress info
@@ -60,3 +58,29 @@ type KeyUpdate struct {
 	Network   string `json:"network" bson:"network"`
 	Network   string `json:"network" bson:"network"`
 	Interface string `json:"interface" bson:"interface"`
 	Interface string `json:"interface" bson:"interface"`
 }
 }
+
+// PeerMqActionType - peer update action type
+type PeerMqActionType string
+
+const (
+	// AddPeer - peer mq action type for adding peers
+	AddPeer PeerMqActionType = "ADD_PEER"
+	// UpdatePeer - peer mq action type for updating peers
+	UpdatePeer PeerMqActionType = "UPDATE_PEER"
+	// RemovePeer - peer mq action type for removing peers
+	RemovePeer PeerMqActionType = "REMOVE_PEER"
+)
+
+// PeerAction - struct for mq peer actions
+type PeerAction struct {
+	Action PeerMqActionType     `json:"action"`
+	Peers  []wgtypes.PeerConfig `json:"peers"`
+}
+
+// FwUpdate - struct for firewall updates
+type FwUpdate struct {
+	IsIngressGw bool                  `json:"is_ingress_gw"`
+	IsEgressGw  bool                  `json:"is_egress_gw"`
+	IngressInfo IngressInfo           `json:"ingress_info"`
+	EgressInfo  map[string]EgressInfo `json:"egress_info"`
+}

+ 9 - 8
models/node.go

@@ -70,6 +70,10 @@ type CommonNode struct {
 	EgressGatewayRanges []string      `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
 	EgressGatewayRanges []string      `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
 	IsIngressGateway    bool          `json:"isingressgateway" yaml:"isingressgateway"`
 	IsIngressGateway    bool          `json:"isingressgateway" yaml:"isingressgateway"`
 	IngressDNS          string        `json:"ingressdns" yaml:"ingressdns"`
 	IngressDNS          string        `json:"ingressdns" yaml:"ingressdns"`
+	IsRelayed           bool          `json:"isrelayed" yaml:"isrelayed"`
+	RelayedBy           string        `json:"relayedby" yaml:"relayedby"`
+	IsRelay             bool          `json:"isrelay" yaml:"isrelay"`
+	RelayedNodes        []string      `json:"relaynodes" yaml:"relayedNodes"`
 	DNSOn               bool          `json:"dnson" yaml:"dnson"`
 	DNSOn               bool          `json:"dnson" yaml:"dnson"`
 	PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"`
 	PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"`
 }
 }
@@ -86,9 +90,6 @@ type Node struct {
 	EgressGatewayRequest    EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
 	EgressGatewayRequest    EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
 	IngressGatewayRange     string               `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
 	IngressGatewayRange     string               `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
 	IngressGatewayRange6    string               `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
 	IngressGatewayRange6    string               `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
-	IsRelayed               bool                 `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
-	IsRelay                 bool                 `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
-	RelayAddrs              []string             `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
 	// == PRO ==
 	// == PRO ==
 	DefaultACL   string    `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
 	DefaultACL   string    `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
 	OwnerID      string    `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
 	OwnerID      string    `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
@@ -350,7 +351,7 @@ func (node *LegacyNode) SetDefaultFailover() {
 }
 }
 
 
 // Node.Fill - fills other node data into calling node data if not set on calling node
 // Node.Fill - fills other node data into calling node data if not set on calling node
-func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present
+func (newNode *Node) Fill(currentNode *Node, isEE bool) { // TODO add new field for nftables present
 	newNode.ID = currentNode.ID
 	newNode.ID = currentNode.ID
 	newNode.HostID = currentNode.HostID
 	newNode.HostID = currentNode.HostID
 	// Revisit the logic for boolean values
 	// Revisit the logic for boolean values
@@ -401,13 +402,13 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable
 	if newNode.Action == "" {
 	if newNode.Action == "" {
 		newNode.Action = currentNode.Action
 		newNode.Action = currentNode.Action
 	}
 	}
-	if newNode.RelayAddrs == nil {
-		newNode.RelayAddrs = currentNode.RelayAddrs
+	if newNode.RelayedNodes == nil {
+		newNode.RelayedNodes = currentNode.RelayedNodes
 	}
 	}
-	if newNode.IsRelay != currentNode.IsRelay {
+	if newNode.IsRelay != currentNode.IsRelay && isEE {
 		newNode.IsRelay = currentNode.IsRelay
 		newNode.IsRelay = currentNode.IsRelay
 	}
 	}
-	if newNode.IsRelayed == currentNode.IsRelayed {
+	if newNode.IsRelayed == currentNode.IsRelayed && isEE {
 		newNode.IsRelayed = currentNode.IsRelayed
 		newNode.IsRelayed = currentNode.IsRelayed
 	}
 	}
 	if newNode.Server == "" {
 	if newNode.Server == "" {

+ 4 - 3
models/structs.go

@@ -153,9 +153,9 @@ type EgressGatewayRequest struct {
 
 
 // RelayRequest - relay request struct
 // RelayRequest - relay request struct
 type RelayRequest struct {
 type RelayRequest struct {
-	NodeID     string   `json:"nodeid" bson:"nodeid"`
-	NetID      string   `json:"netid" bson:"netid"`
-	RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs"`
+	NodeID       string   `json:"nodeid"`
+	NetID        string   `json:"netid"`
+	RelayedNodes []string `json:"relayednodes"`
 }
 }
 
 
 // HostRelayRequest - struct for host relay creation
 // HostRelayRequest - struct for host relay creation
@@ -205,6 +205,7 @@ type HostPull struct {
 	Peers        []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
 	Peers        []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
 	ServerConfig ServerConfig         `json:"server_config" yaml:"server_config"`
 	ServerConfig ServerConfig         `json:"server_config" yaml:"server_config"`
 	PeerIDs      PeerMap              `json:"peer_ids,omitempty" yaml:"peer_ids,omitempty"`
 	PeerIDs      PeerMap              `json:"peer_ids,omitempty" yaml:"peer_ids,omitempty"`
+	FwUpdate     FwUpdate             `json:"fw_update" yaml:"fw_update"`
 }
 }
 
 
 // NodeGet - struct for a single node get response
 // NodeGet - struct for a single node get response

+ 18 - 4
mq/handlers.go

@@ -10,6 +10,7 @@ import (
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
@@ -59,9 +60,17 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 		return
 		return
 	}
 	}
 	if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
 	if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
-		if err = PublishPeerUpdate(); err != nil {
-			slog.Warn("error updating peers when node informed the server of an interface change", "nodeid", currentNode.ID, "error", err)
+		h, err := logic.GetHost(newNode.HostID.String())
+		if err != nil {
+			return
+		}
+		if err = BroadcastAddOrUpdatePeer(h, &newNode, true); err != nil {
+			logger.Log(0, "error updating peers when node", currentNode.ID.String(), "informed the server of an interface change", err.Error())
 		}
 		}
+		if nodes, err := logic.GetNetworkNodes(newNode.Network); err == nil {
+			FlushNetworkPeersToHost(h, &newNode, nodes)
+		}
+
 	}
 	}
 
 
 	slog.Info("updated node", "id", id, "newnodeid", newNode.ID)
 	slog.Info("updated node", "id", id, "newnodeid", newNode.ID)
@@ -107,10 +116,15 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 						return
 						return
 					}
 					}
 				}
 				}
-				if err = PublishSingleHostPeerUpdate(context.Background(), currentHost, nil, nil); err != nil {
-					slog.Error("failed peers publish after join acknowledged", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
+				// flush peers to host
+				nodes, err := logic.GetNetworkNodes(hu.Node.Network)
+				if err != nil {
 					return
 					return
 				}
 				}
+				err = FlushNetworkPeersToHost(&hu.Host, &hu.Node, nodes)
+				if err != nil {
+					logger.Log(0, "failed to flush peers to host: ", err.Error())
+				}
 				if err = handleNewNodeDNS(&hu.Host, &hu.Node); err != nil {
 				if err = handleNewNodeDNS(&hu.Host, &hu.Node); err != nil {
 					slog.Error("failed to send dns update after node added to host", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
 					slog.Error("failed to send dns update after node added to host", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
 					return
 					return

+ 248 - 0
mq/publishers.go

@@ -5,12 +5,15 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"net"
 	"time"
 	"time"
 
 
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
 
 
 // PublishPeerUpdate --- determines and publishes a peer update to all the hosts
 // PublishPeerUpdate --- determines and publishes a peer update to all the hosts
@@ -108,6 +111,242 @@ func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deleted
 	return publish(host, fmt.Sprintf("peers/host/%s/%s", host.ID.String(), servercfg.GetServer()), data)
 	return publish(host, fmt.Sprintf("peers/host/%s/%s", host.ID.String(), servercfg.GetServer()), data)
 }
 }
 
 
+// FlushNetworkPeersToHost - sends all the peers in the network to the host.
+func FlushNetworkPeersToHost(host *models.Host, hNode *models.Node, networkNodes []models.Node) error {
+	logger.Log(0, "flushing network peers to host: ", host.ID.String(), hNode.Network)
+	addPeerAction := models.PeerAction{
+		Action: models.AddPeer,
+		Peers:  []wgtypes.PeerConfig{},
+	}
+	rmPeerAction := models.PeerAction{
+		Action: models.RemovePeer,
+		Peers:  []wgtypes.PeerConfig{},
+	}
+	for _, node := range networkNodes {
+		if node.ID == hNode.ID {
+			// skip self
+			continue
+		}
+		peerHost, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			continue
+		}
+
+		if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(hNode.ID.String()), nodeacls.NodeID(node.ID.String())) ||
+			hNode.Action == models.NODE_DELETE || hNode.PendingDelete || !hNode.Connected {
+			// remove peer if not allowed
+			rmPeerAction.Peers = append(rmPeerAction.Peers, wgtypes.PeerConfig{
+				PublicKey: peerHost.PublicKey,
+				Remove:    true,
+			})
+			continue
+
+		}
+		peerCfg := wgtypes.PeerConfig{
+			PublicKey: peerHost.PublicKey,
+			Endpoint: &net.UDPAddr{
+				IP:   peerHost.EndpointIP,
+				Port: logic.GetPeerListenPort(peerHost),
+			},
+			PersistentKeepaliveInterval: &node.PersistentKeepalive,
+			ReplaceAllowedIPs:           true,
+			AllowedIPs:                  logic.GetAllowedIPs(hNode, &node, nil),
+		}
+		addPeerAction.Peers = append(addPeerAction.Peers, peerCfg)
+	}
+	if hNode.IsIngressGateway {
+		extPeers, _, err := logic.GetExtPeers(hNode)
+		if err == nil {
+			addPeerAction.Peers = append(addPeerAction.Peers, extPeers...)
+		}
+
+	}
+	if len(rmPeerAction.Peers) > 0 {
+		data, err := json.Marshal(rmPeerAction)
+		if err != nil {
+			return err
+		}
+		publish(host, fmt.Sprintf("peer/host/%s/%s", host.ID.String(), servercfg.GetServer()), data)
+	}
+	if len(addPeerAction.Peers) > 0 {
+		data, err := json.Marshal(addPeerAction)
+		if err != nil {
+			return err
+		}
+		publish(host, fmt.Sprintf("peer/host/%s/%s", host.ID.String(), servercfg.GetServer()), data)
+	}
+	// send fw update if gw host
+	if hNode.IsIngressGateway || hNode.IsEgressGateway {
+		f, err := logic.GetFwUpdate(host)
+		if err == nil {
+			PublishFwUpdate(host, &f)
+		}
+
+	}
+	return nil
+}
+
+// BroadcastDelPeer - notifys all the hosts in the network to remove peer
+func BroadcastDelPeer(host *models.Host, network string) error {
+	nodes, err := logic.GetNetworkNodes(network)
+	if err != nil {
+		return err
+	}
+	p := models.PeerAction{
+		Action: models.RemovePeer,
+		Peers: []wgtypes.PeerConfig{
+			{
+				PublicKey: host.PublicKey,
+				Remove:    true,
+			},
+		},
+	}
+	data, err := json.Marshal(p)
+	if err != nil {
+		return err
+	}
+	for _, nodeI := range nodes {
+		if nodeI.HostID == host.ID {
+			// skip self...
+			continue
+		}
+		peerHost, err := logic.GetHost(nodeI.HostID.String())
+		if err == nil {
+			publish(peerHost, fmt.Sprintf("peer/host/%s/%s", peerHost.ID.String(), servercfg.GetServer()), data)
+			if nodeI.IsIngressGateway || nodeI.IsEgressGateway {
+				go func(peerHost models.Host) {
+					f, err := logic.GetFwUpdate(&peerHost)
+					if err == nil {
+						PublishFwUpdate(&peerHost, &f)
+					}
+				}(*peerHost)
+			}
+		}
+	}
+	return nil
+}
+
+// BroadcastAclUpdate - sends new acl updates to peers
+func BroadcastAclUpdate(network string) error {
+	nodes, err := logic.GetNetworkNodes(network)
+	if err != nil {
+		return err
+	}
+	for _, nodeI := range nodes {
+		nodeI := nodeI
+		h, err := logic.GetHost(nodeI.HostID.String())
+		if err == nil {
+			go FlushNetworkPeersToHost(h, &nodeI, nodes)
+		}
+	}
+	return err
+}
+
+// BroadcastAddOrUpdatePeer - notifys the hosts in the network to add or update peer.
+func BroadcastAddOrUpdatePeer(host *models.Host, node *models.Node, update bool) error {
+	nodes, err := logic.GetNetworkNodes(node.Network)
+	if err != nil {
+		return err
+	}
+
+	p := models.PeerAction{
+		Action: models.AddPeer,
+		Peers: []wgtypes.PeerConfig{
+			{
+				PublicKey: host.PublicKey,
+				Endpoint: &net.UDPAddr{
+					IP:   host.EndpointIP,
+					Port: logic.GetPeerListenPort(host),
+				},
+				PersistentKeepaliveInterval: &node.PersistentKeepalive,
+				ReplaceAllowedIPs:           true,
+			},
+		},
+	}
+	if update {
+		p.Action = models.UpdatePeer
+	}
+	for _, nodeI := range nodes {
+		if nodeI.ID.String() == node.ID.String() {
+			// skip self...
+			continue
+		}
+		// update allowed ips, according to the peer node
+		p.Peers[0].AllowedIPs = logic.GetAllowedIPs(&nodeI, node, nil)
+		if update && (!nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(nodeI.ID.String())) ||
+			node.Action == models.NODE_DELETE || node.PendingDelete || !node.Connected) {
+			// remove peer
+			p.Action = models.RemovePeer
+			p.Peers[0].Remove = true
+		}
+		data, err := json.Marshal(p)
+		if err != nil {
+			continue
+		}
+		peerHost, err := logic.GetHost(nodeI.HostID.String())
+		if err == nil {
+			publish(peerHost, fmt.Sprintf("peer/host/%s/%s", peerHost.ID.String(), servercfg.GetServer()), data)
+		}
+		if nodeI.IsIngressGateway || nodeI.IsEgressGateway {
+			go func(peerHost models.Host) {
+				f, err := logic.GetFwUpdate(&peerHost)
+				if err == nil {
+					PublishFwUpdate(&peerHost, &f)
+				}
+			}(*peerHost)
+
+		}
+
+	}
+	return nil
+}
+
+// BroadcastExtClient - publishes msg to add/updates ext client in the network
+func BroadcastExtClient(ingressHost *models.Host, ingressNode *models.Node) error {
+
+	nodes, err := logic.GetNetworkNodes(ingressNode.Network)
+	if err != nil {
+		return err
+	}
+	//flush peers to ingress host
+	go FlushNetworkPeersToHost(ingressHost, ingressNode, nodes)
+	// broadcast to update ingress peer to other hosts
+	go BroadcastAddOrUpdatePeer(ingressHost, ingressNode, true)
+	return nil
+}
+
+// BroadcastDelExtClient - published msg to remove ext client from network
+func BroadcastDelExtClient(ingressHost *models.Host, ingressNode *models.Node, extclients []models.ExtClient) error {
+	// TODO - send fw update
+	go BroadcastAddOrUpdatePeer(ingressHost, ingressNode, true)
+	peers := []wgtypes.PeerConfig{}
+	for _, extclient := range extclients {
+		extPubKey, err := wgtypes.ParseKey(extclient.PublicKey)
+		if err != nil {
+			continue
+		}
+		peers = append(peers, wgtypes.PeerConfig{
+			PublicKey: extPubKey,
+			Remove:    true,
+		})
+
+	}
+	p := models.PeerAction{
+		Action: models.RemovePeer,
+		Peers:  peers,
+	}
+
+	data, err := json.Marshal(p)
+	if err != nil {
+		return err
+	}
+	err = publish(ingressHost, fmt.Sprintf("peer/host/%s/%s", ingressHost.ID.String(), servercfg.GetServer()), data)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
 // NodeUpdate -- publishes a node update
 // NodeUpdate -- publishes a node update
 func NodeUpdate(node *models.Node) error {
 func NodeUpdate(node *models.Node) error {
 	host, err := logic.GetHost(node.HostID.String())
 	host, err := logic.GetHost(node.HostID.String())
@@ -346,6 +585,15 @@ func PublishHostDNSUpdate(old, new *models.Host, networks []string) error {
 	return nil
 	return nil
 }
 }
 
 
+// PublishFwUpdate - publishes fw update to host
+func PublishFwUpdate(gwHost *models.Host, f *models.FwUpdate) error {
+	data, err := json.Marshal(f)
+	if err != nil {
+		return err
+	}
+	return publish(gwHost, fmt.Sprintf("fw/host/%s/%s", gwHost.ID.String(), servercfg.GetServer()), data)
+}
+
 func pushMetricsToExporter(metrics models.Metrics) error {
 func pushMetricsToExporter(metrics models.Metrics) error {
 	logger.Log(2, "----> Pushing metrics to exporter")
 	logger.Log(2, "----> Pushing metrics to exporter")
 	data, err := json.Marshal(metrics)
 	data, err := json.Marshal(metrics)

+ 238 - 0
mq/relay.go

@@ -0,0 +1,238 @@
+package mq
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
+
+// PubPeerUpdate publishes a peer update to the client
+// relay is set to a newly created relay node or nil for other peer updates
+func PubPeerUpdate(client, relay *models.Client, peers []models.Client) {
+	p := models.PeerAction{
+		Action: models.UpdatePeer,
+	}
+	if client.Node.IsRelay {
+		pubRelayUpdate(client, peers)
+		return
+	}
+	if relay != nil {
+		if client.Node.RelayedBy == relay.Node.ID.String() {
+			pubRelayedUpdate(client, relay, peers)
+			return
+		}
+	}
+	for _, peer := range peers {
+		if client.Host.ID == peer.Host.ID {
+			continue
+		}
+		update := wgtypes.PeerConfig{
+			PublicKey:         peer.Host.PublicKey,
+			ReplaceAllowedIPs: true,
+			Endpoint: &net.UDPAddr{
+				IP:   peer.Host.EndpointIP,
+				Port: peer.Host.ListenPort,
+			},
+			PersistentKeepaliveInterval: &peer.Node.PersistentKeepalive,
+		}
+		update.AllowedIPs = append(update.AllowedIPs, logic.AddAllowedIPs(&peer)...)
+		if relay != nil {
+			if peer.Node.IsRelayed && peer.Node.RelayedBy == relay.Node.ID.String() {
+				update.Remove = true
+			}
+		}
+		if peer.Node.IsRelay {
+			update.AllowedIPs = append(update.AllowedIPs, getRelayAllowedIPs(peer)...)
+		}
+		p.Peers = append(p.Peers, update)
+	}
+	data, err := json.Marshal(p)
+	if err != nil {
+		logger.Log(0, "marshal peer update", err.Error())
+		return
+	}
+	publish(&client.Host, fmt.Sprintf("peer/host/%s/%s", client.Host.ID.String(), servercfg.GetServer()), data)
+}
+
+// getRelayAllowedIPs returns the list of allowedips for a given peer that is a relay
+func getRelayAllowedIPs(peer models.Client) []net.IPNet {
+	var relayIPs []net.IPNet
+	for _, relayed := range peer.Node.RelayedNodes {
+		node, err := logic.GetNodeByID(relayed)
+		if err != nil {
+			logger.Log(0, "retrieve relayed node", err.Error())
+			continue
+		}
+		if node.Address.IP != nil {
+			node.Address.Mask = net.CIDRMask(32, 32)
+			relayIPs = append(relayIPs, node.Address)
+		}
+		if node.Address6.IP != nil {
+			node.Address.Mask = net.CIDRMask(128, 128)
+			relayIPs = append(relayIPs, node.Address6)
+		}
+		if node.IsRelay {
+			relayIPs = append(relayIPs, getRelayAllowedIPs(peer)...)
+		}
+		if node.IsEgressGateway {
+			relayIPs = append(relayIPs, getEgressIPs(peer)...)
+		}
+		if node.IsIngressGateway {
+			relayIPs = append(relayIPs, getIngressIPs(peer)...)
+		}
+	}
+	return relayIPs
+}
+
+// getEgressIPs returns the additional allowedips (egress ranges) that need
+// to be included for an egress gateway peer
+func getEgressIPs(peer models.Client) []net.IPNet {
+	var egressIPs []net.IPNet
+	for _, egressRange := range peer.Node.EgressGatewayRanges {
+		ip, cidr, err := net.ParseCIDR(egressRange)
+		if err != nil {
+			logger.Log(0, "parse egress range", err.Error())
+			continue
+		}
+		cidr.IP = ip
+		egressIPs = append(egressIPs, *cidr)
+	}
+	return egressIPs
+}
+
+// getIngressIPs returns the additional allowedips (ext client addresses) that need
+// to be included for an ingress gateway peer
+// TODO:  add ExtraAllowedIPs
+func getIngressIPs(peer models.Client) []net.IPNet {
+	var ingressIPs []net.IPNet
+	extclients, err := logic.GetNetworkExtClients(peer.Node.Network)
+	if err != nil {
+		return ingressIPs
+	}
+	for _, ec := range extclients {
+		if ec.IngressGatewayID == peer.Node.ID.String() {
+			if ec.Address != "" {
+				ip, cidr, err := net.ParseCIDR(ec.Address)
+				if err != nil {
+					continue
+				}
+				cidr.IP = ip
+				ingressIPs = append(ingressIPs, *cidr)
+			}
+			if ec.Address6 != "" {
+				ip, cidr, err := net.ParseCIDR(ec.Address6)
+				if err != nil {
+					continue
+				}
+				cidr.IP = ip
+				ingressIPs = append(ingressIPs, *cidr)
+			}
+		}
+	}
+	return ingressIPs
+}
+
+// pubRelayedUpdate - publish peer update to a node (client) that is relayed by the relay
+func pubRelayedUpdate(client, relay *models.Client, peers []models.Client) {
+	//verify
+	if !logic.StringSliceContains(relay.Node.RelayedNodes, client.Node.ID.String()) {
+		logger.Log(0, "invalid call to pubRelayed update", client.Host.Name, relay.Host.Name)
+		return
+	}
+	//remove all nodes except relay
+	p := models.PeerAction{
+		Action: models.RemovePeer,
+	}
+	for _, peer := range peers {
+		if peer.Host.ID == relay.Host.ID || peer.Host.ID == client.Host.ID {
+			continue
+		}
+		update := wgtypes.PeerConfig{
+			PublicKey: peer.Host.PublicKey,
+			Remove:    true,
+		}
+		p.Peers = append(p.Peers, update)
+	}
+	data, err := json.Marshal(p)
+	if err != nil {
+		logger.Log(0, "marshal peer update", err.Error())
+		return
+	}
+	publish(&client.Host, fmt.Sprintf("peer/host/%s/%s", client.Host.ID.String(), servercfg.GetServer()), data)
+	//update the relay peer
+	p = models.PeerAction{
+		Action: models.UpdatePeer,
+	}
+	update := wgtypes.PeerConfig{
+		PublicKey:         relay.Host.PublicKey,
+		ReplaceAllowedIPs: true,
+		Endpoint: &net.UDPAddr{
+			IP:   relay.Host.EndpointIP,
+			Port: relay.Host.ListenPort,
+		},
+		PersistentKeepaliveInterval: &relay.Node.PersistentKeepalive,
+	}
+	if relay.Node.Address.IP != nil {
+		relay.Node.Address.Mask = net.CIDRMask(32, 32)
+		update.AllowedIPs = append(update.AllowedIPs, relay.Node.Address)
+	}
+	if relay.Node.Address6.IP != nil {
+		relay.Node.Address6.Mask = net.CIDRMask(128, 128)
+		update.AllowedIPs = append(update.AllowedIPs, relay.Node.Address6)
+	}
+	p.Peers = append(p.Peers, update)
+	// add all other peers to allowed ips
+	for _, peer := range peers {
+		if peer.Host.ID == relay.Host.ID || peer.Host.ID == client.Host.ID {
+			continue
+		}
+		update.AllowedIPs = append(update.AllowedIPs, logic.AddAllowedIPs(&peer)...)
+	}
+	p.Peers = append(p.Peers, update)
+	data, err = json.Marshal(p)
+	if err != nil {
+		logger.Log(0, "marshal peer update", err.Error())
+		return
+	}
+	publish(&client.Host, fmt.Sprintf("peer/host/%s/%s", client.Host.ID.String(), servercfg.GetServer()), data)
+}
+
+// pubRelayUpdate - publish peer update to a relay
+func pubRelayUpdate(client *models.Client, peers []models.Client) {
+	if !client.Node.IsRelay {
+		return
+	}
+	// add all peers to allowedips
+	p := models.PeerAction{
+		Action: models.UpdatePeer,
+	}
+	for _, peer := range peers {
+		if peer.Host.ID == client.Host.ID {
+			continue
+		}
+		update := wgtypes.PeerConfig{
+			PublicKey:         peer.Host.PublicKey,
+			ReplaceAllowedIPs: true,
+			Remove:            false,
+			Endpoint: &net.UDPAddr{
+				IP:   peer.Host.EndpointIP,
+				Port: peer.Host.ListenPort,
+			},
+			PersistentKeepaliveInterval: &peer.Node.PersistentKeepalive,
+		}
+		update.AllowedIPs = append(update.AllowedIPs, logic.AddAllowedIPs(&peer)...)
+		p.Peers = append(p.Peers, update)
+	}
+	data, err := json.Marshal(p)
+	if err != nil {
+		logger.Log(0, "marshal peer update", err.Error())
+		return
+	}
+	publish(&client.Host, fmt.Sprintf("peer/host/%s/%s", client.Host.ID.String(), servercfg.GetServer()), data)
+}