From 50525aaf8d0f9124dbc1426a945ebf610fae6988 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 25 Nov 2025 14:19:36 -0500 Subject: [PATCH] Formatting of peers and dns worked Former-commit-id: 9281fbd22286a75e24ff93f883196a4bba5f1d81 --- dns/override/dns_override_unix.go | 27 +- dns/platform/detect_darwin.go | 30 --- dns/platform/detect_unix.go | 195 ++++++++++----- dns/platform/detect_windows.go | 34 --- olm/olm.go | 394 ++++------------------------- olm/types.go | 67 +---- peers/manager.go | 401 ++++++++++++++++++++++++++++++ {olm => peers}/peer.go | 24 +- peers/types.go | 57 +++++ 9 files changed, 674 insertions(+), 555 deletions(-) delete mode 100644 dns/platform/detect_darwin.go delete mode 100644 dns/platform/detect_windows.go create mode 100644 peers/manager.go rename {olm => peers}/peer.go (86%) create mode 100644 peers/types.go diff --git a/dns/override/dns_override_unix.go b/dns/override/dns_override_unix.go index ed724a2..5c99083 100644 --- a/dns/override/dns_override_unix.go +++ b/dns/override/dns_override_unix.go @@ -14,7 +14,7 @@ import ( var configurator platform.DNSConfigurator // SetupDNSOverride configures the system DNS to use the DNS proxy on Linux/FreeBSD -// Tries systemd-resolved, NetworkManager, resolvconf, or falls back to /etc/resolv.conf +// Detects the DNS manager by reading /etc/resolv.conf and verifying runtime availability func SetupDNSOverride(interfaceName string, dnsProxy *dns.DNSProxy) error { if dnsProxy == nil { return fmt.Errorf("DNS proxy is nil") @@ -22,34 +22,35 @@ func SetupDNSOverride(interfaceName string, dnsProxy *dns.DNSProxy) error { var err error - // Try systemd-resolved first (most modern) - if platform.IsSystemdResolvedAvailable() && interfaceName != "" { + // Detect which DNS manager is in use by checking /etc/resolv.conf and runtime availability + managerType := platform.DetectDNSManager(interfaceName) + logger.Info("Detected DNS manager: %s", managerType.String()) + + // Create configurator based on detected manager + switch managerType { + case platform.SystemdResolvedManager: configurator, err = platform.NewSystemdResolvedDNSConfigurator(interfaceName) if err == nil { logger.Info("Using systemd-resolved DNS configurator") return setDNS(dnsProxy, configurator) } - logger.Debug("systemd-resolved not available: %v", err) - } + logger.Warn("Failed to create systemd-resolved configurator: %v, falling back", err) - // Try NetworkManager (common on desktops) - if platform.IsNetworkManagerAvailable() && interfaceName != "" { + case platform.NetworkManagerManager: configurator, err = platform.NewNetworkManagerDNSConfigurator(interfaceName) if err == nil { - logger.Info("Using NetworkManager DNS configurator") + logger.Info("************************************Using NetworkManager DNS configurator") return setDNS(dnsProxy, configurator) } - logger.Debug("NetworkManager not available: %v", err) - } + logger.Warn("Failed to create NetworkManager configurator: %v, falling back", err) - // Try resolvconf (common on older systems) - if platform.IsResolvconfAvailable() && interfaceName != "" { + case platform.ResolvconfManager: configurator, err = platform.NewResolvconfDNSConfigurator(interfaceName) if err == nil { logger.Info("Using resolvconf DNS configurator") return setDNS(dnsProxy, configurator) } - logger.Debug("resolvconf not available: %v", err) + logger.Warn("Failed to create resolvconf configurator: %v, falling back", err) } // Fall back to direct file manipulation diff --git a/dns/platform/detect_darwin.go b/dns/platform/detect_darwin.go deleted file mode 100644 index ee931f5..0000000 --- a/dns/platform/detect_darwin.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:build darwin && !ios - -package dns - -import "fmt" - -// DetectBestConfigurator returns the macOS DNS configurator -func DetectBestConfigurator(ifaceName string) (DNSConfigurator, error) { - return NewDarwinDNSConfigurator() -} - -// GetSystemDNS returns the current system DNS servers -func GetSystemDNS() ([]string, error) { - configurator, err := NewDarwinDNSConfigurator() - if err != nil { - return nil, fmt.Errorf("create configurator: %w", err) - } - - servers, err := configurator.GetCurrentDNS() - if err != nil { - return nil, fmt.Errorf("get current DNS: %w", err) - } - - var result []string - for _, server := range servers { - result = append(result, server.String()) - } - - return result, nil -} diff --git a/dns/platform/detect_unix.go b/dns/platform/detect_unix.go index 53cc4e3..035690d 100644 --- a/dns/platform/detect_unix.go +++ b/dns/platform/detect_unix.go @@ -3,90 +3,149 @@ package dns import ( - "fmt" - "net/netip" + "bufio" + "io" "os" "strings" + + "github.com/fosrl/newt/logger" ) -// DetectBestConfigurator detects and returns the most appropriate DNS configurator for the system -// ifaceName is optional and only used for NetworkManager, systemd-resolved, and resolvconf -func DetectBestConfigurator(ifaceName string) (DNSConfigurator, error) { - // Try systemd-resolved first (most modern) - if IsSystemdResolvedAvailable() && ifaceName != "" { - if configurator, err := NewSystemdResolvedDNSConfigurator(ifaceName); err == nil { - return configurator, nil - } - } +const defaultResolvConfPath = "/etc/resolv.conf" - // Try NetworkManager (common on desktops) - if IsNetworkManagerAvailable() && ifaceName != "" { - if configurator, err := NewNetworkManagerDNSConfigurator(ifaceName); err == nil { - return configurator, nil - } - } +// DNSManagerType represents the type of DNS manager detected +type DNSManagerType int - // Try resolvconf (common on older systems) - if IsResolvconfAvailable() && ifaceName != "" { - if configurator, err := NewResolvconfDNSConfigurator(ifaceName); err == nil { - return configurator, nil - } - } +const ( + // UnknownManager indicates we couldn't determine the DNS manager + UnknownManager DNSManagerType = iota + // SystemdResolvedManager indicates systemd-resolved is managing DNS + SystemdResolvedManager + // NetworkManagerManager indicates NetworkManager is managing DNS + NetworkManagerManager + // ResolvconfManager indicates resolvconf is managing DNS + ResolvconfManager + // FileManager indicates direct file management (no DNS manager) + FileManager +) - // Fall back to direct file manipulation - return NewFileDNSConfigurator() -} - -// Helper functions for checking system state - -// IsSystemdResolvedRunning checks if systemd-resolved is running -func IsSystemdResolvedRunning() bool { - // Check if stub resolver is configured - servers, err := readResolvConfDNS() +// DetectDNSManagerFromFile reads /etc/resolv.conf to determine which DNS manager is in use +// This provides a hint based on comments in the file, similar to Netbird's approach +func DetectDNSManagerFromFile() DNSManagerType { + file, err := os.Open(defaultResolvConfPath) if err != nil { - return false + return UnknownManager } + defer file.Close() - // systemd-resolved uses 127.0.0.53 - stubAddr := netip.MustParseAddr("127.0.0.53") - for _, server := range servers { - if server == stubAddr { - return true - } - } - - return false -} - -// readResolvConfDNS reads DNS servers from /etc/resolv.conf -func readResolvConfDNS() ([]netip.Addr, error) { - content, err := os.ReadFile("/etc/resolv.conf") - if err != nil { - return nil, fmt.Errorf("read resolv.conf: %w", err) - } - - var servers []netip.Addr - lines := strings.Split(string(content), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - if line == "" || strings.HasPrefix(line, "#") { + scanner := bufio.NewScanner(file) + for scanner.Scan() { + text := scanner.Text() + if len(text) == 0 { continue } - if strings.HasPrefix(line, "nameserver") { - fields := strings.Fields(line) - if len(fields) >= 2 { - if addr, err := netip.ParseAddr(fields[1]); err == nil { - servers = append(servers, addr) - } - } + // If we hit a non-comment line, default to file-based + if text[0] != '#' { + return FileManager + } + + // Check for DNS manager signatures in comments + if strings.Contains(text, "NetworkManager") { + return NetworkManagerManager + } + + if strings.Contains(text, "systemd-resolved") { + return SystemdResolvedManager + } + + if strings.Contains(text, "resolvconf") { + return ResolvconfManager } } - return servers, nil + if err := scanner.Err(); err != nil && err != io.EOF { + return UnknownManager + } + + // No indicators found, assume file-based management + return FileManager } -// GetSystemDNS returns the current system DNS servers -func GetSystemDNS() ([]netip.Addr, error) { - return readResolvConfDNS() +// String returns a human-readable name for the DNS manager type +func (d DNSManagerType) String() string { + switch d { + case SystemdResolvedManager: + return "systemd-resolved" + case NetworkManagerManager: + return "NetworkManager" + case ResolvconfManager: + return "resolvconf" + case FileManager: + return "file" + default: + return "unknown" + } +} + +// DetectDNSManager combines file detection with runtime availability checks +// to determine the best DNS configurator to use +func DetectDNSManager(interfaceName string) DNSManagerType { + // First check what the file suggests + fileHint := DetectDNSManagerFromFile() + + // Verify the hint with runtime checks + switch fileHint { + case SystemdResolvedManager: + // Verify systemd-resolved is actually running + if IsSystemdResolvedAvailable() { + return SystemdResolvedManager + } + logger.Warn("dns platform: Found systemd-resolved but it is not running. Falling back to file...") + os.Exit(0) + return FileManager + + case NetworkManagerManager: + // Verify NetworkManager is actually running + if IsNetworkManagerAvailable() { + return NetworkManagerManager + } + logger.Warn("dns platform: Found network manager but it is not running. Falling back to file...") + return FileManager + + case ResolvconfManager: + // Verify resolvconf is available + if IsResolvconfAvailable() { + return ResolvconfManager + } + // If resolvconf is mentioned but not available, fall back to file + return FileManager + + case FileManager: + // File suggests direct file management + // But we should still check if a manager is available that wasn't mentioned + if IsSystemdResolvedAvailable() && interfaceName != "" { + return SystemdResolvedManager + } + if IsNetworkManagerAvailable() && interfaceName != "" { + return NetworkManagerManager + } + if IsResolvconfAvailable() && interfaceName != "" { + return ResolvconfManager + } + return FileManager + + default: + // Unknown - do runtime detection + if IsSystemdResolvedAvailable() && interfaceName != "" { + return SystemdResolvedManager + } + if IsNetworkManagerAvailable() && interfaceName != "" { + return NetworkManagerManager + } + if IsResolvconfAvailable() && interfaceName != "" { + return ResolvconfManager + } + return FileManager + } } diff --git a/dns/platform/detect_windows.go b/dns/platform/detect_windows.go deleted file mode 100644 index d62cc94..0000000 --- a/dns/platform/detect_windows.go +++ /dev/null @@ -1,34 +0,0 @@ -//go:build windows - -package dns - -import "fmt" - -// DetectBestConfigurator returns the Windows DNS configurator -// ifaceName should be the network interface GUID on Windows -func DetectBestConfigurator(ifaceName string) (DNSConfigurator, error) { - if ifaceName == "" { - return nil, fmt.Errorf("interface GUID is required for Windows") - } - return newWindowsDNSConfiguratorFromGUID(ifaceName) -} - -// GetSystemDNS returns the current system DNS servers for the given interface -func GetSystemDNS(ifaceName string) ([]string, error) { - configurator, err := newWindowsDNSConfiguratorFromGUID(ifaceName) - if err != nil { - return nil, fmt.Errorf("create configurator: %w", err) - } - - servers, err := configurator.GetCurrentDNS() - if err != nil { - return nil, fmt.Errorf("get current DNS: %w", err) - } - - var result []string - for _, server := range servers { - result = append(result, server.String()) - } - - return result, nil -} diff --git a/olm/olm.go b/olm/olm.go index a77c7ac..32145e4 100644 --- a/olm/olm.go +++ b/olm/olm.go @@ -21,6 +21,7 @@ import ( dnsOverride "github.com/fosrl/olm/dns/override" "github.com/fosrl/olm/network" "github.com/fosrl/olm/peermonitor" + "github.com/fosrl/olm/peers" "github.com/fosrl/olm/websocket" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun" @@ -48,6 +49,7 @@ var ( globalCtx context.Context stopRegister func() stopPing chan struct{} + peerManager *peers.PeerManager ) func Init(ctx context.Context, config GlobalConfig) { @@ -464,33 +466,16 @@ func StartTunnel(config TunnelConfig) { interfaceIP, ) + peerManager = peers.NewPeerManager(dev, peerMonitor, dnsProxy, interfaceName, privateKey) + for i := range wgData.Sites { - site := &wgData.Sites[i] // Use a pointer to modify the struct in the slice + site := wgData.Sites[i] apiServer.UpdatePeerStatus(site.SiteId, false, 0, site.Endpoint, false) - if err := ConfigurePeer(dev, *site, privateKey, endpoint); err != nil { - logger.Error("Failed to configure peer: %v", err) + if err := peerManager.AddPeer(site, endpoint); err != nil { + logger.Error("Failed to add peer: %v", err) return } - if err := network.AddRouteForServerIP(site.ServerIP, interfaceName); err != nil { // this is something for darwin only thats required - logger.Error("Failed to add route for peer: %v", err) - return - } - if err := network.AddRoutes(site.RemoteSubnets, interfaceName); err != nil { - logger.Error("Failed to add routes for remote subnets: %v", err) - return - } - - for _, alias := range site.Aliases { - // try to parse the alias address into net.IP - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - - dnsProxy.AddDNSRecord(alias.Alias, address) - } logger.Info("Configured peer %s", site.PublicKey) } @@ -528,41 +513,21 @@ func StartTunnel(config TunnelConfig) { return } - var updateData SiteConfig + var updateData peers.SiteConfig if err := json.Unmarshal(jsonData, &updateData); err != nil { logger.Error("Error unmarshaling update data: %v", err) return } - // Update the peer in WireGuard - if dev == nil { - logger.Error("WireGuard device not initialized") - return - } - - // Find the existing peer to merge updates with - var existingPeer *SiteConfig - var peerIndex int - for i, site := range wgData.Sites { - if site.SiteId == updateData.SiteId { - existingPeer = &wgData.Sites[i] - peerIndex = i - break - } - } - - if existingPeer == nil { + // Get existing peer from PeerManager + existingPeer, exists := peerManager.GetPeer(updateData.SiteId) + if !exists { logger.Error("Peer with site ID %d not found", updateData.SiteId) return } - // Store old values for comparison - oldRemoteSubnets := existingPeer.RemoteSubnets - oldPublicKey := existingPeer.PublicKey - // Create updated site config by merging with existing data - // Only update fields that are provided (non-empty/non-zero) - siteConfig := *existingPeer // Start with existing data + siteConfig := existingPeer if updateData.Endpoint != "" { siteConfig.Endpoint = updateData.Endpoint @@ -580,37 +545,13 @@ func StartTunnel(config TunnelConfig) { siteConfig.RemoteSubnets = updateData.RemoteSubnets } - // If the public key has changed, remove the old peer first - if siteConfig.PublicKey != oldPublicKey { - logger.Info("Public key changed for site %d, removing old peer with key %s", updateData.SiteId, oldPublicKey) - if err := RemovePeer(dev, updateData.SiteId, oldPublicKey); err != nil { - logger.Error("Failed to remove old peer: %v", err) - return - } - } - - if err := ConfigurePeer(dev, siteConfig, privateKey, endpoint); err != nil { + if err := peerManager.UpdatePeer(siteConfig, endpoint); err != nil { logger.Error("Failed to update peer: %v", err) return } - // Handle remote subnet route changes - if !stringSlicesEqual(oldRemoteSubnets, siteConfig.RemoteSubnets) { - if err := network.RemoveRoutes(oldRemoteSubnets); err != nil { - logger.Error("Failed to remove old remote subnet routes: %v", err) - // Continue anyway to add new routes - } - - // Add new remote subnet routes - if err := network.AddRoutes(siteConfig.RemoteSubnets, interfaceName); err != nil { - logger.Error("Failed to add new remote subnet routes: %v", err) - return - } - } - // Update successful logger.Info("Successfully updated peer for site %d", updateData.SiteId) - wgData.Sites[peerIndex] = siteConfig }) // Handler for adding a new peer @@ -623,46 +564,19 @@ func StartTunnel(config TunnelConfig) { return } - var siteConfig SiteConfig + var siteConfig peers.SiteConfig if err := json.Unmarshal(jsonData, &siteConfig); err != nil { logger.Error("Error unmarshaling add data: %v", err) return } - // Add the peer to WireGuard - if dev == nil { - logger.Error("WireGuard device not initialized") - return - } - - if err := ConfigurePeer(dev, siteConfig, privateKey, endpoint); err != nil { + if err := peerManager.AddPeer(siteConfig, endpoint); err != nil { logger.Error("Failed to add peer: %v", err) return } - if err := network.AddRouteForServerIP(siteConfig.ServerIP, interfaceName); err != nil { - logger.Error("Failed to add route for new peer: %v", err) - return - } - if err := network.AddRoutes(siteConfig.RemoteSubnets, interfaceName); err != nil { - logger.Error("Failed to add routes for remote subnets: %v", err) - return - } - for _, alias := range siteConfig.Aliases { - // try to parse the alias address into net.IP - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - - dnsProxy.AddDNSRecord(alias.Alias, address) - } // Add successful logger.Info("Successfully added peer for site %d", siteConfig.SiteId) - - // Update WgData with the new peer - wgData.Sites = append(wgData.Sites, siteConfig) }) // Handler for removing a peer @@ -675,69 +589,19 @@ func StartTunnel(config TunnelConfig) { return } - var removeData PeerRemove + var removeData peers.PeerRemove if err := json.Unmarshal(jsonData, &removeData); err != nil { logger.Error("Error unmarshaling remove data: %v", err) return } - // Find the peer to remove - var peerToRemove *SiteConfig - var newSites []SiteConfig - - for _, site := range wgData.Sites { - if site.SiteId == removeData.SiteId { - peerToRemove = &site - } else { - newSites = append(newSites, site) - } - } - - if peerToRemove == nil { - logger.Error("Peer with site ID %d not found", removeData.SiteId) - return - } - - // Remove the peer from WireGuard - if dev == nil { - logger.Error("WireGuard device not initialized") - return - } - if err := RemovePeer(dev, removeData.SiteId, peerToRemove.PublicKey); err != nil { + if err := peerManager.RemovePeer(removeData.SiteId); err != nil { logger.Error("Failed to remove peer: %v", err) - // Send error response if needed return } - // Remove route for the peer - err = network.RemoveRouteForServerIP(peerToRemove.ServerIP, interfaceName) - if err != nil { - logger.Error("Failed to remove route for peer: %v", err) - return - } - - // Remove routes for remote subnets - if err := network.RemoveRoutes(peerToRemove.RemoteSubnets); err != nil { - logger.Error("Failed to remove routes for remote subnets: %v", err) - return - } - - for _, alias := range peerToRemove.Aliases { - // try to parse the alias address into net.IP - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - - dnsProxy.RemoveDNSRecord(alias.Alias, address) - } - // Remove successful logger.Info("Successfully removed peer for site %d", removeData.SiteId) - - // Update WgData to remove the peer - wgData.Sites = newSites }) // Handler for adding remote subnets to a peer @@ -750,77 +614,25 @@ func StartTunnel(config TunnelConfig) { return } - var addSubnetsData PeerAdd + var addSubnetsData peers.PeerAdd if err := json.Unmarshal(jsonData, &addSubnetsData); err != nil { logger.Error("Error unmarshaling add-remote-subnets data: %v", err) return } - // Find the peer to update - var peerIndex = -1 - for i, site := range wgData.Sites { - if site.SiteId == addSubnetsData.SiteId { - peerIndex = i - break - } - } - - if peerIndex == -1 { - logger.Error("Peer with site ID %d not found", addSubnetsData.SiteId) - return - } - - // Add new subnets to the peer's remote subnets (avoiding duplicates) - existingSubnets := make(map[string]bool) - for _, subnet := range wgData.Sites[peerIndex].RemoteSubnets { - existingSubnets[subnet] = true - } - - var newSubnets []string + // Add new subnets for _, subnet := range addSubnetsData.RemoteSubnets { - if !existingSubnets[subnet] { - newSubnets = append(newSubnets, subnet) - wgData.Sites[peerIndex].RemoteSubnets = append(wgData.Sites[peerIndex].RemoteSubnets, subnet) + if err := peerManager.AddRemoteSubnet(addSubnetsData.SiteId, subnet); err != nil { + logger.Error("Failed to add allowed IP %s: %v", subnet, err) } } - if len(newSubnets) == 0 { - logger.Info("No new subnets to add for site %d (all already exist)", addSubnetsData.SiteId) - // Still process aliases even if no new subnets - } else { - // Add routes for the new subnets - if err := network.AddRoutes(newSubnets, interfaceName); err != nil { - logger.Error("Failed to add routes for new remote subnets: %v", err) - return - } - logger.Info("Successfully added %d remote subnet(s) to peer %d", len(newSubnets), addSubnetsData.SiteId) - } - - // Add new aliases to the peer's aliases (avoiding duplicates) - existingAliases := make(map[string]bool) - for _, alias := range wgData.Sites[peerIndex].Aliases { - existingAliases[alias.Alias] = true - } - - var newAliases []Alias + // Add new aliases for _, alias := range addSubnetsData.Aliases { - if !existingAliases[alias.Alias] { - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - - // Add DNS record - dnsProxy.AddDNSRecord(alias.Alias, address) - newAliases = append(newAliases, alias) - wgData.Sites[peerIndex].Aliases = append(wgData.Sites[peerIndex].Aliases, alias) + if err := peerManager.AddAlias(addSubnetsData.SiteId, alias); err != nil { + logger.Error("Failed to add alias %s: %v", alias.Alias, err) } } - - if len(newAliases) > 0 { - logger.Info("Successfully added %d alias(es) to peer %d", len(newAliases), addSubnetsData.SiteId) - } }) // Handler for removing remote subnets from a peer @@ -833,90 +645,25 @@ func StartTunnel(config TunnelConfig) { return } - var removeSubnetsData RemovePeerData + var removeSubnetsData peers.RemovePeerData if err := json.Unmarshal(jsonData, &removeSubnetsData); err != nil { logger.Error("Error unmarshaling remove-remote-subnets data: %v", err) return } - // Find the peer to update - var peerIndex = -1 - for i, site := range wgData.Sites { - if site.SiteId == removeSubnetsData.SiteId { - peerIndex = i - break - } - } - - if peerIndex == -1 { - logger.Error("Peer with site ID %d not found", removeSubnetsData.SiteId) - return - } - - // Create a map of subnets to remove for quick lookup - subnetsToRemove := make(map[string]bool) + // Remove subnets for _, subnet := range removeSubnetsData.RemoteSubnets { - subnetsToRemove[subnet] = true - } - - // Filter out the subnets to remove - var updatedSubnets []string - var removedSubnets []string - for _, subnet := range wgData.Sites[peerIndex].RemoteSubnets { - if subnetsToRemove[subnet] { - removedSubnets = append(removedSubnets, subnet) - } else { - updatedSubnets = append(updatedSubnets, subnet) + if err := peerManager.RemoveRemoteSubnet(removeSubnetsData.SiteId, subnet); err != nil { + logger.Error("Failed to remove allowed IP %s: %v", subnet, err) } } - if len(removedSubnets) == 0 { - logger.Info("No subnets to remove for site %d (none matched)", removeSubnetsData.SiteId) - // Still process aliases even if no subnets to remove - } else { - // Remove routes for the removed subnets - if err := network.RemoveRoutes(removedSubnets); err != nil { - logger.Error("Failed to remove routes for remote subnets: %v", err) - return - } - - // Update the peer's remote subnets - wgData.Sites[peerIndex].RemoteSubnets = updatedSubnets - logger.Info("Successfully removed %d remote subnet(s) from peer %d", len(removedSubnets), removeSubnetsData.SiteId) - } - - // Create a map of aliases to remove for quick lookup - aliasesToRemove := make(map[string]bool) + // Remove aliases for _, alias := range removeSubnetsData.Aliases { - aliasesToRemove[alias.Alias] = true - } - - // Filter out the aliases to remove - var updatedAliases []Alias - var removedAliases []Alias - for _, alias := range wgData.Sites[peerIndex].Aliases { - if aliasesToRemove[alias.Alias] { - removedAliases = append(removedAliases, alias) - } else { - updatedAliases = append(updatedAliases, alias) + if err := peerManager.RemoveAlias(removeSubnetsData.SiteId, alias.Alias); err != nil { + logger.Error("Failed to remove alias %s: %v", alias.Alias, err) } } - - if len(removedAliases) > 0 { - // Remove DNS records for the removed aliases - for _, alias := range removedAliases { - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - dnsProxy.RemoveDNSRecord(alias.Alias, address) - } - - // Update the peer's aliases - wgData.Sites[peerIndex].Aliases = updatedAliases - logger.Info("Successfully removed %d alias(es) from peer %d", len(removedAliases), removeSubnetsData.SiteId) - } }) // Handler for updating remote subnets of a peer (remove old, add new in one operation) @@ -929,82 +676,41 @@ func StartTunnel(config TunnelConfig) { return } - var updateSubnetsData UpdatePeerData + var updateSubnetsData peers.UpdatePeerData if err := json.Unmarshal(jsonData, &updateSubnetsData); err != nil { logger.Error("Error unmarshaling update-remote-subnets data: %v", err) return } - // Find the peer to update - var peerIndex = -1 - for i, site := range wgData.Sites { - if site.SiteId == updateSubnetsData.SiteId { - peerIndex = i - break + // Remove old subnets + for _, subnet := range updateSubnetsData.OldRemoteSubnets { + if err := peerManager.RemoveRemoteSubnet(updateSubnetsData.SiteId, subnet); err != nil { + logger.Error("Failed to remove allowed IP %s: %v", subnet, err) } } - if peerIndex == -1 { - logger.Error("Peer with site ID %d not found", updateSubnetsData.SiteId) - return - } - - // First, remove routes for old subnets - if len(updateSubnetsData.OldRemoteSubnets) > 0 { - if err := network.RemoveRoutes(updateSubnetsData.OldRemoteSubnets); err != nil { - logger.Error("Failed to remove routes for old remote subnets: %v", err) - return + // Add new subnets + for _, subnet := range updateSubnetsData.NewRemoteSubnets { + if err := peerManager.AddRemoteSubnet(updateSubnetsData.SiteId, subnet); err != nil { + logger.Error("Failed to add allowed IP %s: %v", subnet, err) } - logger.Info("Removed %d old remote subnet(s) from peer %d", len(updateSubnetsData.OldRemoteSubnets), updateSubnetsData.SiteId) } - // Then, add routes for new subnets - if len(updateSubnetsData.NewRemoteSubnets) > 0 { - if err := network.AddRoutes(updateSubnetsData.NewRemoteSubnets, interfaceName); err != nil { - logger.Error("Failed to add routes for new remote subnets: %v", err) - // Attempt to rollback by re-adding old routes - if rollbackErr := network.AddRoutes(updateSubnetsData.OldRemoteSubnets, interfaceName); rollbackErr != nil { - logger.Error("Failed to rollback old routes: %v", rollbackErr) - } - return + // Remove old aliases + for _, alias := range updateSubnetsData.OldAliases { + if err := peerManager.RemoveAlias(updateSubnetsData.SiteId, alias.Alias); err != nil { + logger.Error("Failed to remove alias %s: %v", alias.Alias, err) } - logger.Info("Added %d new remote subnet(s) to peer %d", len(updateSubnetsData.NewRemoteSubnets), updateSubnetsData.SiteId) } - // Finally, update the peer's remote subnets in wgData - wgData.Sites[peerIndex].RemoteSubnets = updateSubnetsData.NewRemoteSubnets - - logger.Info("Successfully updated remote subnets for peer %d (removed %d, added %d)", - updateSubnetsData.SiteId, len(updateSubnetsData.OldRemoteSubnets), len(updateSubnetsData.NewRemoteSubnets)) - - // Remove DNS records for old aliases - if len(updateSubnetsData.OldAliases) > 0 { - for _, alias := range updateSubnetsData.OldAliases { - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid old alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - dnsProxy.RemoveDNSRecord(alias.Alias, address) + // Add new aliases + for _, alias := range updateSubnetsData.NewAliases { + if err := peerManager.AddAlias(updateSubnetsData.SiteId, alias); err != nil { + logger.Error("Failed to add alias %s: %v", alias.Alias, err) } - logger.Info("Removed %d old alias(es) from peer %d", len(updateSubnetsData.OldAliases), updateSubnetsData.SiteId) } - // Add DNS records for new aliases - if len(updateSubnetsData.NewAliases) > 0 { - for _, alias := range updateSubnetsData.NewAliases { - address := net.ParseIP(alias.AliasAddress) - if address == nil { - logger.Warn("Invalid new alias address for %s: %s", alias.Alias, alias.AliasAddress) - continue - } - dnsProxy.AddDNSRecord(alias.Alias, address) - } - logger.Info("Added %d new alias(es) to peer %d", len(updateSubnetsData.NewAliases), updateSubnetsData.SiteId) - } - - // Update the peer's aliases in wgData - wgData.Sites[peerIndex].Aliases = updateSubnetsData.NewAliases + logger.Info("Successfully updated remote subnets and aliases for peer %d", updateSubnetsData.SiteId) }) olm.RegisterHandler("olm/wg/peer/relay", func(msg websocket.WSMessage) { @@ -1016,7 +722,7 @@ func StartTunnel(config TunnelConfig) { return } - var relayData RelayPeerData + var relayData peers.RelayPeerData if err := json.Unmarshal(jsonData, &relayData); err != nil { logger.Error("Error unmarshaling relay data: %v", err) return diff --git a/olm/types.go b/olm/types.go index 48df08a..28ba4e2 100644 --- a/olm/types.go +++ b/olm/types.go @@ -1,11 +1,15 @@ package olm -import "time" +import ( + "time" + + "github.com/fosrl/olm/peers" +) type WgData struct { - Sites []SiteConfig `json:"sites"` - TunnelIP string `json:"tunnelIP"` - UtilitySubnet string `json:"utilitySubnet"` // this is for things like the DNS server, and alias addresses + Sites []peers.SiteConfig `json:"sites"` + TunnelIP string `json:"tunnelIP"` + UtilitySubnet string `json:"utilitySubnet"` // this is for things like the DNS server, and alias addresses } type HolePunchMessage struct { @@ -27,61 +31,6 @@ type EncryptedHolePunchMessage struct { Ciphertext []byte `json:"ciphertext"` } -// PeerAction represents a request to add, update, or remove a peer -type PeerAction struct { - Action string `json:"action"` // "add", "update", or "remove" - SiteInfo SiteConfig `json:"siteInfo"` // Site configuration information -} - -// UpdatePeerData represents the data needed to update a peer -type SiteConfig struct { - SiteId int `json:"siteId"` - Endpoint string `json:"endpoint,omitempty"` - PublicKey string `json:"publicKey,omitempty"` - ServerIP string `json:"serverIP,omitempty"` - ServerPort uint16 `json:"serverPort,omitempty"` - RemoteSubnets []string `json:"remoteSubnets,omitempty"` // optional, array of subnets that this site can access - Aliases []Alias `json:"aliases,omitempty"` // optional, array of alias configurations -} - -type Alias struct { - Alias string `json:"alias"` // the alias name - AliasAddress string `json:"aliasAddress"` // the alias IP address -} - -// RemovePeer represents the data needed to remove a peer -type PeerRemove struct { - SiteId int `json:"siteId"` -} - -type RelayPeerData struct { - SiteId int `json:"siteId"` - Endpoint string `json:"endpoint"` - PublicKey string `json:"publicKey"` -} - -// PeerAdd represents the data needed to add remote subnets to a peer -type PeerAdd struct { - SiteId int `json:"siteId"` - RemoteSubnets []string `json:"remoteSubnets"` // subnets to add - Aliases []Alias `json:"aliases,omitempty"` // aliases to add -} - -// RemovePeerData represents the data needed to remove remote subnets from a peer -type RemovePeerData struct { - SiteId int `json:"siteId"` - RemoteSubnets []string `json:"remoteSubnets"` // subnets to remove - Aliases []Alias `json:"aliases,omitempty"` // aliases to remove -} - -type UpdatePeerData struct { - SiteId int `json:"siteId"` - OldRemoteSubnets []string `json:"oldRemoteSubnets"` // old list of remote subnets - NewRemoteSubnets []string `json:"newRemoteSubnets"` // new list of remote subnets - OldAliases []Alias `json:"oldAliases,omitempty"` // old list of aliases - NewAliases []Alias `json:"newAliases,omitempty"` // new list of aliases -} - type GlobalConfig struct { // Logging LogLevel string diff --git a/peers/manager.go b/peers/manager.go new file mode 100644 index 0000000..acf630a --- /dev/null +++ b/peers/manager.go @@ -0,0 +1,401 @@ +package peers + +import ( + "fmt" + "net" + "sync" + + "github.com/fosrl/newt/logger" + "github.com/fosrl/olm/dns" + "github.com/fosrl/olm/network" + "github.com/fosrl/olm/peermonitor" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +type PeerManager struct { + mu sync.RWMutex + device *device.Device + peers map[int]SiteConfig + peerMonitor *peermonitor.PeerMonitor + dnsProxy *dns.DNSProxy + interfaceName string + privateKey wgtypes.Key +} + +func NewPeerManager(dev *device.Device, monitor *peermonitor.PeerMonitor, dnsProxy *dns.DNSProxy, interfaceName string, privateKey wgtypes.Key) *PeerManager { + return &PeerManager{ + device: dev, + peers: make(map[int]SiteConfig), + peerMonitor: monitor, + dnsProxy: dnsProxy, + interfaceName: interfaceName, + privateKey: privateKey, + } +} + +func (pm *PeerManager) GetPeer(siteId int) (SiteConfig, bool) { + pm.mu.RLock() + defer pm.mu.RUnlock() + peer, ok := pm.peers[siteId] + return peer, ok +} + +func (pm *PeerManager) GetAllPeers() []SiteConfig { + pm.mu.RLock() + defer pm.mu.RUnlock() + peers := make([]SiteConfig, 0, len(pm.peers)) + for _, peer := range pm.peers { + peers = append(peers, peer) + } + return peers +} + +func (pm *PeerManager) AddPeer(siteConfig SiteConfig, endpoint string) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + // build the allowed IPs list from the remote subnets and aliases and add them to the peer + allowedIPs := make([]string, 0, len(siteConfig.RemoteSubnets)+len(siteConfig.Aliases)) + allowedIPs = append(allowedIPs, siteConfig.RemoteSubnets...) + for _, alias := range siteConfig.Aliases { + allowedIPs = append(allowedIPs, alias.AliasAddress+"/32") + } + siteConfig.AllowedIps = allowedIPs + + if err := ConfigurePeer(pm.device, siteConfig, pm.privateKey, endpoint, pm.peerMonitor); err != nil { + return err + } + + if err := network.AddRouteForServerIP(siteConfig.ServerIP, pm.interfaceName); err != nil { + logger.Error("Failed to add route for server IP: %v", err) + } + if err := network.AddRoutes(siteConfig.RemoteSubnets, pm.interfaceName); err != nil { + logger.Error("Failed to add routes for remote subnets: %v", err) + } + for _, alias := range siteConfig.Aliases { + address := net.ParseIP(alias.AliasAddress) + if address == nil { + continue + } + pm.dnsProxy.AddDNSRecord(alias.Alias, address) + } + + pm.peers[siteConfig.SiteId] = siteConfig + return nil +} + +func (pm *PeerManager) RemovePeer(siteId int) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + if err := RemovePeer(pm.device, siteId, peer.PublicKey, pm.peerMonitor); err != nil { + return err + } + + if err := network.RemoveRouteForServerIP(peer.ServerIP, pm.interfaceName); err != nil { + logger.Error("Failed to remove route for server IP: %v", err) + } + + if err := network.RemoveRoutes(peer.RemoteSubnets); err != nil { + logger.Error("Failed to remove routes for remote subnets: %v", err) + } + + // For aliases + for _, alias := range peer.Aliases { + address := net.ParseIP(alias.AliasAddress) + if address == nil { + continue + } + pm.dnsProxy.RemoveDNSRecord(alias.Alias, address) + } + + delete(pm.peers, siteId) + return nil +} + +func (pm *PeerManager) UpdatePeer(siteConfig SiteConfig, endpoint string) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + oldPeer, exists := pm.peers[siteConfig.SiteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteConfig.SiteId) + } + + // If public key changed, remove old peer first + if siteConfig.PublicKey != oldPeer.PublicKey { + if err := RemovePeer(pm.device, siteConfig.SiteId, oldPeer.PublicKey, pm.peerMonitor); err != nil { + logger.Error("Failed to remove old peer: %v", err) + } + } + + if err := ConfigurePeer(pm.device, siteConfig, pm.privateKey, endpoint, pm.peerMonitor); err != nil { + return err + } + + // Handle remote subnet route changes + // Calculate added and removed subnets + oldSubnets := make(map[string]bool) + for _, s := range oldPeer.RemoteSubnets { + oldSubnets[s] = true + } + newSubnets := make(map[string]bool) + for _, s := range siteConfig.RemoteSubnets { + newSubnets[s] = true + } + + var addedSubnets []string + var removedSubnets []string + + for s := range newSubnets { + if !oldSubnets[s] { + addedSubnets = append(addedSubnets, s) + } + } + for s := range oldSubnets { + if !newSubnets[s] { + removedSubnets = append(removedSubnets, s) + } + } + + // Remove routes for removed subnets + if len(removedSubnets) > 0 { + if err := network.RemoveRoutes(removedSubnets); err != nil { + logger.Error("Failed to remove routes: %v", err) + } + } + + // Add routes for added subnets + if len(addedSubnets) > 0 { + if err := network.AddRoutes(addedSubnets, pm.interfaceName); err != nil { + logger.Error("Failed to add routes: %v", err) + } + } + + // Update aliases + // Remove old aliases + for _, alias := range oldPeer.Aliases { + address := net.ParseIP(alias.AliasAddress) + if address == nil { + continue + } + pm.dnsProxy.RemoveDNSRecord(alias.Alias, address) + } + // Add new aliases + for _, alias := range siteConfig.Aliases { + address := net.ParseIP(alias.AliasAddress) + if address == nil { + continue + } + pm.dnsProxy.AddDNSRecord(alias.Alias, address) + } + + pm.peers[siteConfig.SiteId] = siteConfig + return nil +} + +// addAllowedIp adds an IP (subnet) to the allowed IPs list of a peer +// and updates WireGuard configuration. Must be called with lock held. +func (pm *PeerManager) addAllowedIp(siteId int, ip string) error { + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + // Check if IP already exists in AllowedIps + for _, allowedIp := range peer.AllowedIps { + if allowedIp == ip { + return nil // Already exists + } + } + + peer.AllowedIps = append(peer.AllowedIps, ip) + + // Update WireGuard + if err := ConfigurePeer(pm.device, peer, pm.privateKey, peer.Endpoint, pm.peerMonitor); err != nil { + return err + } + + pm.peers[siteId] = peer + return nil +} + +// removeAllowedIp removes an IP (subnet) from the allowed IPs list of a peer +// and updates WireGuard configuration. Must be called with lock held. +func (pm *PeerManager) removeAllowedIp(siteId int, cidr string) error { + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + found := false + + // Remove from AllowedIps + newAllowedIps := make([]string, 0, len(peer.AllowedIps)) + for _, allowedIp := range peer.AllowedIps { + if allowedIp == cidr { + found = true + continue + } + newAllowedIps = append(newAllowedIps, allowedIp) + } + + if !found { + return nil // Not found + } + + peer.AllowedIps = newAllowedIps + + // Update WireGuard + if err := ConfigurePeer(pm.device, peer, pm.privateKey, peer.Endpoint, pm.peerMonitor); err != nil { + return err + } + + pm.peers[siteId] = peer + return nil +} + +// AddRemoteSubnet adds an IP (subnet) to the allowed IPs list of a peer +func (pm *PeerManager) AddRemoteSubnet(siteId int, cidr string) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + // Check if IP already exists in RemoteSubnets + for _, subnet := range peer.RemoteSubnets { + if subnet == cidr { + return nil // Already exists + } + } + + peer.RemoteSubnets = append(peer.RemoteSubnets, cidr) + + // Add to allowed IPs + if err := pm.addAllowedIp(siteId, cidr); err != nil { + return err + } + + // Add route + if err := network.AddRoutes([]string{cidr}, pm.interfaceName); err != nil { + return err + } + + return nil +} + +// RemoveRemoteSubnet removes an IP (subnet) from the allowed IPs list of a peer +func (pm *PeerManager) RemoveRemoteSubnet(siteId int, ip string) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + found := false + + // Remove from RemoteSubnets + newSubnets := make([]string, 0, len(peer.RemoteSubnets)) + for _, subnet := range peer.RemoteSubnets { + if subnet == ip { + found = true + continue + } + newSubnets = append(newSubnets, subnet) + } + + if !found { + return nil // Not found + } + + peer.RemoteSubnets = newSubnets + + // Remove from allowed IPs + if err := pm.removeAllowedIp(siteId, ip); err != nil { + return err + } + + // Remove route + if err := network.RemoveRoutes([]string{ip}); err != nil { + return err + } + + pm.peers[siteId] = peer + + return nil +} + +// AddAlias adds an alias to a peer +func (pm *PeerManager) AddAlias(siteId int, alias Alias) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + peer.Aliases = append(peer.Aliases, alias) + pm.peers[siteId] = peer + + address := net.ParseIP(alias.AliasAddress) + if address != nil { + pm.dnsProxy.AddDNSRecord(alias.Alias, address) + } + + // Add an allowed IP for the alias + if err := pm.addAllowedIp(siteId, alias.AliasAddress+"/32"); err != nil { + return err + } + + return nil +} + +// RemoveAlias removes an alias from a peer +func (pm *PeerManager) RemoveAlias(siteId int, aliasName string) error { + pm.mu.Lock() + defer pm.mu.Unlock() + + peer, exists := pm.peers[siteId] + if !exists { + return fmt.Errorf("peer with site ID %d not found", siteId) + } + + var aliasToRemove *Alias + newAliases := make([]Alias, 0, len(peer.Aliases)) + for _, a := range peer.Aliases { + if a.Alias == aliasName { + aliasToRemove = &a + continue + } + newAliases = append(newAliases, a) + } + + if aliasToRemove != nil { + address := net.ParseIP(aliasToRemove.AliasAddress) + if address != nil { + pm.dnsProxy.RemoveDNSRecord(aliasName, address) + } + } + + // remove the allowed IP for the alias + if err := pm.removeAllowedIp(siteId, aliasToRemove.AliasAddress+"/32"); err != nil { + return err + } + + peer.Aliases = newAliases + pm.peers[siteId] = peer + + return nil +} diff --git a/olm/peer.go b/peers/peer.go similarity index 86% rename from olm/peer.go rename to peers/peer.go index 73feb69..116d199 100644 --- a/olm/peer.go +++ b/peers/peer.go @@ -1,4 +1,4 @@ -package olm +package peers import ( "fmt" @@ -14,7 +14,7 @@ import ( ) // ConfigurePeer sets up or updates a peer within the WireGuard device -func ConfigurePeer(dev *device.Device, siteConfig SiteConfig, privateKey wgtypes.Key, endpoint string) error { +func ConfigurePeer(dev *device.Device, siteConfig SiteConfig, privateKey wgtypes.Key, endpoint string, peerMonitor *peermonitor.PeerMonitor) error { siteHost, err := util.ResolveDomain(formatEndpoint(siteConfig.Endpoint)) if err != nil { return fmt.Errorf("failed to resolve endpoint for site %d: %v", siteConfig.SiteId, err) @@ -33,10 +33,13 @@ func ConfigurePeer(dev *device.Device, siteConfig SiteConfig, privateKey wgtypes var allowedIPs []string allowedIPs = append(allowedIPs, allowedIpStr) - // If we have anything in remoteSubnets, add those as well - if len(siteConfig.RemoteSubnets) > 0 { - // Add each remote subnet - for _, subnet := range siteConfig.RemoteSubnets { + // Use AllowedIps if available, otherwise fall back to RemoteSubnets for backwards compatibility + subnetsToAdd := siteConfig.AllowedIps + + // If we have anything to add, process them + if len(subnetsToAdd) > 0 { + // Add each subnet + for _, subnet := range subnetsToAdd { subnet = strings.TrimSpace(subnet) if subnet != "" { allowedIPs = append(allowedIPs, subnet) @@ -96,7 +99,7 @@ func ConfigurePeer(dev *device.Device, siteConfig SiteConfig, privateKey wgtypes } // RemovePeer removes a peer from the WireGuard device -func RemovePeer(dev *device.Device, siteId int, publicKey string) error { +func RemovePeer(dev *device.Device, siteId int, publicKey string, peerMonitor *peermonitor.PeerMonitor) error { // Construct WireGuard config to remove the peer var configBuilder strings.Builder configBuilder.WriteString(fmt.Sprintf("public_key=%s\n", util.FixKey(publicKey))) @@ -118,3 +121,10 @@ func RemovePeer(dev *device.Device, siteId int, publicKey string) error { return nil } + +func formatEndpoint(endpoint string) string { + if strings.Contains(endpoint, ":") { + return endpoint + } + return endpoint + ":51820" +} diff --git a/peers/types.go b/peers/types.go new file mode 100644 index 0000000..f984ba6 --- /dev/null +++ b/peers/types.go @@ -0,0 +1,57 @@ +package peers + +// PeerAction represents a request to add, update, or remove a peer +type PeerAction struct { + Action string `json:"action"` // "add", "update", or "remove" + SiteInfo SiteConfig `json:"siteInfo"` // Site configuration information +} + +// UpdatePeerData represents the data needed to update a peer +type SiteConfig struct { + SiteId int `json:"siteId"` + Endpoint string `json:"endpoint,omitempty"` + PublicKey string `json:"publicKey,omitempty"` + ServerIP string `json:"serverIP,omitempty"` + ServerPort uint16 `json:"serverPort,omitempty"` + RemoteSubnets []string `json:"remoteSubnets,omitempty"` // optional, array of subnets that this site can access + AllowedIps []string `json:"allowedIps,omitempty"` // optional, array of allowed IPs for the peer + Aliases []Alias `json:"aliases,omitempty"` // optional, array of alias configurations +} + +type Alias struct { + Alias string `json:"alias"` // the alias name + AliasAddress string `json:"aliasAddress"` // the alias IP address +} + +// RemovePeer represents the data needed to remove a peer +type PeerRemove struct { + SiteId int `json:"siteId"` +} + +type RelayPeerData struct { + SiteId int `json:"siteId"` + Endpoint string `json:"endpoint"` + PublicKey string `json:"publicKey"` +} + +// PeerAdd represents the data needed to add remote subnets to a peer +type PeerAdd struct { + SiteId int `json:"siteId"` + RemoteSubnets []string `json:"remoteSubnets"` // subnets to add + Aliases []Alias `json:"aliases,omitempty"` // aliases to add +} + +// RemovePeerData represents the data needed to remove remote subnets from a peer +type RemovePeerData struct { + SiteId int `json:"siteId"` + RemoteSubnets []string `json:"remoteSubnets"` // subnets to remove + Aliases []Alias `json:"aliases,omitempty"` // aliases to remove +} + +type UpdatePeerData struct { + SiteId int `json:"siteId"` + OldRemoteSubnets []string `json:"oldRemoteSubnets"` // old list of remote subnets + NewRemoteSubnets []string `json:"newRemoteSubnets"` // new list of remote subnets + OldAliases []Alias `json:"oldAliases,omitempty"` // old list of aliases + NewAliases []Alias `json:"newAliases,omitempty"` // new list of aliases +}