From 5e64dfc4eb5b65c282093f6eb7ea290c1bcb70e8 Mon Sep 17 00:00:00 2001 From: GRMrGecko Date: Sun, 6 Jul 2025 22:45:34 -0500 Subject: [PATCH] Add log rotation and updated support for log output configuration. --- config.go | 1244 +++++++++++++++++++++------------------- go.mod | 45 +- go.sum | 70 ++- main.go | 2 +- vxlan/vxlan.pb.go | 2 +- vxlan/vxlan_grpc.pb.go | 2 +- 6 files changed, 740 insertions(+), 625 deletions(-) diff --git a/config.go b/config.go index 5b0ad68..c022042 100644 --- a/config.go +++ b/config.go @@ -1,584 +1,660 @@ -package main - -import ( - "fmt" - "io" - "net" - "net/netip" - "os" - "path/filepath" - "time" - - "github.com/kkyr/fig" - "github.com/shibukawa/configdir" - log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" -) - -// Just bare minimal configuration for reading in cli calls. -type ConfigMinimal struct { - RPCPath string `fig:"rpc_path" yaml:"rpc_path"` - Update *UpdateConfig `fig:"update" yaml:"update"` - Log *LogConfig `fig:"log" yaml:"log"` -} - -// Main configuration structure. -type Config struct { - ConfigMinimal - Listeners []ListenerConfig `fig:"listeners" yaml:"listeners"` -} - -type LogConfig struct { - Level string `fig:"level" yaml:"level" enum:"debug,info,warn,error" default:"info"` - Type string `fig:"type" yaml:"type" enum:"json,console" default:"console"` - Path string `fig:"path" yaml:"path" default:""` -} - -// Configuration for updating. -type UpdateConfig struct { - Owner string `fig:"owner" yaml:"owner"` - Repo string `fig:"repo" yaml:"repo"` - Disabled bool `fig:"disabled" yaml:"disabled"` - CurrentVersion string `fig:"-" yaml:"-"` - ShouldRelaunch bool `fig:"-" yaml:"-"` - PreUpdate func() `fig:"-" yaml:"-"` - IsSuccessMsg func(string) bool `fig:"-" yaml:"-"` - StartupTimeout time.Duration `fig:"-" yaml:"-"` - AbortUpdate func() `fig:"-" yaml:"-"` -} - -// Listener configuration structure. -type ListenerConfig struct { - Name string `fig:"name" yaml:"name"` - Address string `fig:"address" yaml:"address"` - MaxMessageSize int `fig:"max_message_size" yaml:"max_message_size"` - Interfaces []InterfaceConfig `fig:"interfaces" yaml:"interfaces"` -} - -// Interface configuration strucuture. -type InterfaceConfig struct { - Name string `fig:"name" yaml:"name"` - VNI uint32 `fig:"vni" yaml:"vni"` - MTU int `fig:"mtu" yaml:"mtu"` - MACAddress string `fig:"mac_address" yaml:"mac_address"` - IPAddressCIDRS []string `fig:"ip_addresess_cidrs" yaml:"ip_addresess_cidrs"` - ARPEntries []ARPEntryConfig `fig:"arp_entries" yaml:"arp_entries"` - MACEntries []MACEntryConfig `fig:"mac_entries" yaml:"mac_entries"` - StaticRoutes []StaticRouteConfig `fig:"static_routes" yaml:"static_routes"` -} - -// Permanent ARP entries. -type ARPEntryConfig struct { - IPAddress string `fig:"ip_address" yaml:"ip_address"` - MACAddress string `fig:"mac_address" yaml:"mac_address"` -} - -// Permanent MAC entries. -type MACEntryConfig struct { - MACAddress string `fig:"mac_address" yaml:"mac_address"` - Destination string `fig:"destination" yaml:"destination"` -} - -// Permanent static routes. -type StaticRouteConfig struct { - Destination string `fig:"destination" yaml:"destination"` - Gateway string `fig:"gateway" yaml:"gateway"` - Metric int `fig:"metric" yaml:"metric"` -} - -// Applies common filters to the read configuration. -func (c *Config) ApplyFilters(configDir string) { - // If the RPC path isn't set, set it to temp dir. - if c.RPCPath == "" { - c.RPCPath = filepath.Join(configDir, fmt.Sprintf("%s.sock", serviceName)) - } - // Check if the RPC socket already exists. - _, err := os.Stat(c.RPCPath) - if err == nil { - // If the socket exists, see if its listening. - _, err = net.Dial("unix", c.RPCPath) - - // If its not listening, remove it to allow us to start. - if err != nil { - os.Remove(c.RPCPath) - } - } -} - -// Get the config path/ -func ConfigPath() (fileDir, fileName string) { - // Find the configuration directory. - configDirs := configdir.New(serviceVendor, serviceName) - folders := configDirs.QueryFolders(configdir.System) - if len(folders) == 0 { - log.Fatalf("Unable to find config path.") - } - - // Find the file name. - fileName = defaultConfigFile - fileDir = folders[0].Path - if flags.ConfigPath != "" { - fileDir, fileName = filepath.Split(flags.ConfigPath) - } - - // Verify directory exists. - if _, ferr := os.Stat(fileDir); ferr != nil { - err := os.MkdirAll(fileDir, 0755) - if err != nil { - log.Error("Failed to make directory:", err) - } - } - return -} - -// Makes the default config for reading. -func DefaultConfig() *Config { - config := new(Config) - config.Update = &UpdateConfig{ - Owner: "grmrgecko", - Repo: "virtual-vxlan", - } - config.Log = flags.Log - return config -} - -// Read configuration file and return the current config. -func ReadMinimalConfig() *Config { - // Setup default minimal config. - config := DefaultConfig() - - // Find the file name. - fileDir, fileName := ConfigPath() - - // Read the configuration file if it exists. - err := fig.Load(&config.ConfigMinimal, fig.File(fileName), fig.Dirs(fileDir)) - // On error, just print as we want to return a default config. - if err != nil { - log.Debug("Unable to load config file:", err) - } - - // Apply config filters. - config.ApplyFilters(fileDir) - - // Apply any log configurations loaded from file. - config.Log.Apply() - return config -} - -// Read configuration file and return the current config. -func ReadConfig() *Config { - // Setup default config. - config := DefaultConfig() - - // Find the file name. - fileDir, fileName := ConfigPath() - - // Read the configuration file if it exists. - err := fig.Load(config, fig.File(fileName), fig.Dirs(fileDir)) - // On error, just print as we want to return a default config. - if err != nil { - log.Debug("Unable to load config file:", err) - } - - // Apply config filters. - config.ApplyFilters(fileDir) - - // Apply any log configurations loaded from file. - config.Log.Apply() - return config -} - -// Apply the supplied configuration file to this app instance. -func ApplyConfig(config *Config) (err error) { - // Find listeners and interfaces that are permanent - // and not in the config being applied. Remove them - // so that we only have configured permanent entries. - { - var listenersToRemove []*Listener - var interfacesToRemove []*Interface - // Lock to prevent other actions. - app.Net.Lock() - for _, listener := range app.Net.Listeners { - if listener.Permanent { - // The address is the pretty name. - addr := listener.PrettyName() - - // Find listeners in the config that match this address. - var found *ListenerConfig - for _, list := range config.Listeners { - if addr == list.Address && listener.Name == list.Name { - found = &list - break - } - } - - // If we found a listener, check its interfaces. - if found != nil { - // Look for interfaces on this listener which - // are no longer in the config. - listener.net.RLock() - for _, iface := range listener.net.interfaces { - if iface.Permanent { - // Match by both name and VNI, either change - // and we need to remake it. - name := iface.Name() - vni := iface.VNI() - - // Loop to find. - foundIfce := false - for _, ifce := range found.Interfaces { - if ifce.Name == name && ifce.VNI == vni { - foundIfce = true - } - } - // If we didn't find this interface, add to remove list. - if !foundIfce { - interfacesToRemove = append(interfacesToRemove, iface) - } - } - } - listener.net.RUnlock() - } else { - // This listener wasn't found, lets remove it. - listenersToRemove = append(listenersToRemove, listener) - } - } - } - - // Unlock net as we're done looking at lists and we - // need it unlocked to remove items. - app.Net.Unlock() - - // Remove listeners not found. - for _, list := range listenersToRemove { - list.Close() - } - - // Remove interfaces not found. - for _, ifce := range interfacesToRemove { - ifce.Close() - } - } - - // Loop through listeners in the config to add/change the - // configurations of both listeners and their interfaces. - for _, listener := range config.Listeners { - // First check to see if an existing listener is there. - var l *Listener - app.Net.Lock() - for _, list := range app.Net.Listeners { - if list.PrettyName() == listener.Address { - l = list - l.Permanent = true - break - } - } - app.Net.Unlock() - - // If no existing listeners, add a new listener. - if l == nil { - l, err = NewListener(listener.Name, listener.Address, listener.MaxMessageSize, true) - if err != nil { - return fmt.Errorf("failed to start listener: %s %v", listener.Address, err) - } - } else { - // If the listener was already existing, update the max message size. - l.SetMaxMessageSize(listener.MaxMessageSize) - } - - // Loop through interfaces on this listener to add/upate them. - for _, iface := range listener.Interfaces { - // See if this interface is already on the listener. - var i *Interface - l.net.RLock() - for _, ifce := range l.net.interfaces { - if ifce.VNI() == iface.VNI { - i = ifce - i.Permanent = true - } - } - l.net.RUnlock() - - // If this interface isn't existing, add it. - if i == nil { - i, err = NewInterface(iface.Name, iface.VNI, iface.MTU, l, true) - if err != nil { - return fmt.Errorf("failed to make interface: %s %v", iface.Name, err) - } - } else { - // If the interface is existing, update the MTU. - i.SetMTU(iface.MTU) - } - - // Parse the interface's MAC address. - mac, err := net.ParseMAC(iface.MACAddress) - if err != nil { - return fmt.Errorf("failed tp parse MAC: %s %v", iface.MACAddress, err) - } - - // Set the interface's MAC address. - err = i.SetMACAddress(mac) - if err != nil { - return fmt.Errorf("failed to set MAC address %s on interface %s: %v", iface.MACAddress, iface.Name, err) - } - - // Parse the interface's IP addresses CIDRs. - var prefixes []netip.Prefix - for _, addr := range iface.IPAddressCIDRS { - prefix, err := netip.ParsePrefix(addr) - if err != nil { - return fmt.Errorf("failed to parse CIDR: %s %v", addr, err) - } - prefixes = append(prefixes, prefix) - } - - // If IP addresses are set for this interface, set them. - if len(prefixes) != 0 { - err = i.SetIPAddresses(prefixes) - if err != nil { - return fmt.Errorf("failed to set IP addresses on interface: %s %v", iface.Name, err) - } - } - - // Flush the ARP table of any permanent entry. - i.tables.Lock() - found := true - for found { - found = false - for p, ent := range i.tables.arp { - if ent.Permanent { - found = true - i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) - break - } - } - } - i.tables.Unlock() - - // Add permanent ARP entries from the config. - for _, ent := range iface.ARPEntries { - addr, err := netip.ParseAddr(ent.IPAddress) - if err != nil { - return fmt.Errorf("failed to parse IP: %s %v", ent.IPAddress, err) - } - mac, err := net.ParseMAC(ent.MACAddress) - if err != nil { - return fmt.Errorf("failed to parse MAC: %s %v", ent.MACAddress, err) - } - i.AddStaticARPEntry(addr, mac, true) - } - - // Flush the MAC table of any permanent entry. - i.tables.Lock() - found = true - for found { - found = false - for p, ent := range i.tables.mac { - if ent.Permanent { - found = true - i.tables.mac = append(i.tables.mac[:p], i.tables.mac[p+1:]...) - break - } - } - } - i.tables.Unlock() - - // Add permanent entries from this config. - for _, ent := range iface.MACEntries { - mac, err := net.ParseMAC(ent.MACAddress) - if err != nil { - return fmt.Errorf("failed to parse MAC: %s %v", ent.MACAddress, err) - } - dst := net.ParseIP(ent.Destination) - if dst == nil { - return fmt.Errorf("failed to parse destination: %s", ent.Destination) - } - i.AddMACEntry(mac, dst, true) - } - - // Flush the route table of any permanent entry. - i.tables.Lock() - found = true - for found { - found = false - for _, ent := range i.tables.route { - if ent.Permanent { - found = true - i.RemoveStaticRoute(ent.Destination, ent.Gateway) - break - } - } - } - i.tables.Unlock() - - // Add permanent routes from this config. - for _, ent := range iface.StaticRoutes { - destination, err := netip.ParsePrefix(ent.Destination) - if err != nil { - return fmt.Errorf("failed to parse destination prefix %s: %v", ent.Destination, err) - } - gateway, err := netip.ParseAddr(ent.Gateway) - if err != nil { - return fmt.Errorf("failed to parse gateway %s: %v", ent.Gateway, err) - } - i.AddStaticRoute(destination, gateway, ent.Metric, true) - } - } - } - - // No errors occurred, configuration is applied. - return nil -} - -// Take the current application state and save permanent configurations -// to the configuration file. -func SaveConfig() error { - // Start a new configuration file. - config := new(Config) - config.RPCPath = app.grpcServer.RPCPath - config.Update = app.UpdateConfig - config.Log = flags.Log - - // Look the global app config during this process. - app.Net.Lock() - defer app.Net.Unlock() - - // Loop through listeners and add permanent listeners to the config. - for _, listener := range app.Net.Listeners { - // If this listener is permanent, add it. - if listener.Permanent { - // Make a listener config. - listnr := ListenerConfig{ - Address: listener.PrettyName(), - MaxMessageSize: listener.MaxMessageSize(), - Name: listener.Name, - } - - // Loop through interfaces on this listener and add to the config. - listener.net.Lock() - for _, iface := range listener.net.interfaces { - // Only add interface if its permanent. - if iface.Permanent { - // Get the MAC Address. - mac, err := iface.GetMACAddress() - if err != nil { - return err - } - // Get the IP addresses on this interface. - ipAddrs, err := iface.GetIPAddresses() - if err != nil { - return err - } - - // Make the config for this interface. - ifce := InterfaceConfig{ - Name: iface.Name(), - VNI: iface.VNI(), - MTU: iface.MTU(), - MACAddress: mac.String(), - } - - // Add the CIDRs for this interface. - for _, addr := range ipAddrs { - ifce.IPAddressCIDRS = append(ifce.IPAddressCIDRS, addr.String()) - } - - // Get and add permanent ARP entries to the config. - for _, ent := range iface.GetARPEntries() { - if ent.Permanent { - entry := ARPEntryConfig{ - IPAddress: ent.Addr.String(), - MACAddress: ent.MAC.String(), - } - ifce.ARPEntries = append(ifce.ARPEntries, entry) - } - } - - // Get and add permanent MAC enties to the config. - for _, ent := range iface.GetMACEntries() { - if ent.Permanent { - entry := MACEntryConfig{ - MACAddress: ent.MAC.String(), - Destination: ent.Dst.IP.String(), - } - ifce.MACEntries = append(ifce.MACEntries, entry) - } - } - - // Get and add permanent static routes to the config. - for _, ent := range iface.GetStaticRoutes() { - if ent.Permanent { - route := StaticRouteConfig{ - Destination: ent.Destination.String(), - Gateway: ent.Gateway.String(), - Metric: ent.Metric, - } - ifce.StaticRoutes = append(ifce.StaticRoutes, route) - } - } - - // Add this interface config to the listener config. - listnr.Interfaces = append(listnr.Interfaces, ifce) - } - } - listener.net.Unlock() - - // Add this listener to the list of listeners in the config. - config.Listeners = append(config.Listeners, listnr) - } - } - - // Encode YAML data. - data, err := yaml.Marshal(config) - if err != nil { - return err - } - - // Find the file name. - fileDir, fileName := ConfigPath() - - // Write the configuration file. - err = os.WriteFile(filepath.Join(fileDir, fileName), data, 0644) - return err -} - -// Apply log config. -func (l *LogConfig) Apply() { - // Apply level. - switch l.Level { - case "debug": - log.SetLevel(log.DebugLevel) - case "info": - log.SetLevel(log.InfoLevel) - case "warn": - log.SetLevel(log.WarnLevel) - default: - log.SetLevel(log.ErrorLevel) - } - - // Apply type. - switch l.Type { - case "json": - log.SetFormatter(&log.JSONFormatter{}) - default: - log.SetFormatter(&log.TextFormatter{}) - } - - // If path isn't set, make it the executable location. - if l.Path == "" { - exe, err := os.Executable() - if err != nil { - panic(err) - } - l.Path = filepath.Join(filepath.Dir(exe), fmt.Sprintf("%s.log", serviceName)) - } - - // Set the log to save to the logpath. - f, err := os.OpenFile(l.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) - if err != nil { - log.Println("Failed to open log file: %s %v", l.Path, err) - } else { - mw := io.MultiWriter(f, os.Stdout) - log.SetOutput(mw) - } -} +package main + +import ( + "fmt" + "io" + "net" + "net/netip" + "os" + "path/filepath" + "runtime" + "time" + + "github.com/kardianos/service" + "github.com/kkyr/fig" + "github.com/shibukawa/configdir" + log "github.com/sirupsen/logrus" + "gopkg.in/natefinch/lumberjack.v2" + "gopkg.in/yaml.v3" +) + +// Just bare minimal configuration for reading in cli calls. +type ConfigMinimal struct { + RPCPath string `fig:"rpc_path" yaml:"rpc_path"` + Update *UpdateConfig `fig:"update" yaml:"update"` + Log *LogConfig `fig:"log" yaml:"log"` +} + +// Main configuration structure. +type Config struct { + ConfigMinimal + Listeners []ListenerConfig `fig:"listeners" yaml:"listeners"` +} + +// Configuration for logging. +type LogConfig struct { + // Limit the log output by the log level. + Level string `fig:"level" yaml:"level" enum:"debug,info,warn,error" default:"info"` + // How should the log output be formatted. + Type string `fig:"type" yaml:"type" enum:"json,console" default:"console"` + // The outputs that the log should go to. Output of `console` will + // go to the stderr. An file path, will log to the file. Using `default-file` + // it'll either save to `/var/log/name.log`, or to the same directory as the + // executable if the path is not writable, or on Windows. + Outputs []string `fig:"outputs" yaml:"outputs" default:"console,default-file"` + // Maximum size of the log file in megabytes before it gets rotated. + MaxSize int `fig:"max_size" yaml:"max_size" default:"1"` + // Maximum number of backups to save. + MaxBackups int `fig:"max_backups" yaml:"max_backups" default:"3"` + // Maximum number of days to retain old log files. + MaxAge int `fig:"max_age" yaml:"max_age" default:"0"` + // Use the logal system time instead of UTC for file names of rotated backups. + LocalTime *bool `fig:"local_time" yaml:"local_time" default:"true"` + // Should the rotated logs be compressed. + Compress *bool `fig:"compress" yaml:"compress" default:"true"` +} + +// Configuration for updating. +type UpdateConfig struct { + Owner string `fig:"owner" yaml:"owner"` + Repo string `fig:"repo" yaml:"repo"` + Disabled bool `fig:"disabled" yaml:"disabled"` + CurrentVersion string `fig:"-" yaml:"-"` + ShouldRelaunch bool `fig:"-" yaml:"-"` + PreUpdate func() `fig:"-" yaml:"-"` + IsSuccessMsg func(string) bool `fig:"-" yaml:"-"` + StartupTimeout time.Duration `fig:"-" yaml:"-"` + AbortUpdate func() `fig:"-" yaml:"-"` +} + +// Listener configuration structure. +type ListenerConfig struct { + Name string `fig:"name" yaml:"name"` + Address string `fig:"address" yaml:"address"` + MaxMessageSize int `fig:"max_message_size" yaml:"max_message_size"` + Interfaces []InterfaceConfig `fig:"interfaces" yaml:"interfaces"` +} + +// Interface configuration strucuture. +type InterfaceConfig struct { + Name string `fig:"name" yaml:"name"` + VNI uint32 `fig:"vni" yaml:"vni"` + MTU int `fig:"mtu" yaml:"mtu"` + MACAddress string `fig:"mac_address" yaml:"mac_address"` + IPAddressCIDRS []string `fig:"ip_addresess_cidrs" yaml:"ip_addresess_cidrs"` + ARPEntries []ARPEntryConfig `fig:"arp_entries" yaml:"arp_entries"` + MACEntries []MACEntryConfig `fig:"mac_entries" yaml:"mac_entries"` + StaticRoutes []StaticRouteConfig `fig:"static_routes" yaml:"static_routes"` +} + +// Permanent ARP entries. +type ARPEntryConfig struct { + IPAddress string `fig:"ip_address" yaml:"ip_address"` + MACAddress string `fig:"mac_address" yaml:"mac_address"` +} + +// Permanent MAC entries. +type MACEntryConfig struct { + MACAddress string `fig:"mac_address" yaml:"mac_address"` + Destination string `fig:"destination" yaml:"destination"` +} + +// Permanent static routes. +type StaticRouteConfig struct { + Destination string `fig:"destination" yaml:"destination"` + Gateway string `fig:"gateway" yaml:"gateway"` + Metric int `fig:"metric" yaml:"metric"` +} + +// Applies common filters to the read configuration. +func (c *Config) ApplyFilters(configDir string) { + // If the RPC path isn't set, set it to temp dir. + if c.RPCPath == "" { + c.RPCPath = filepath.Join(configDir, fmt.Sprintf("%s.sock", serviceName)) + } + // Check if the RPC socket already exists. + _, err := os.Stat(c.RPCPath) + if err == nil { + // If the socket exists, see if its listening. + _, err = net.Dial("unix", c.RPCPath) + + // If its not listening, remove it to allow us to start. + if err != nil { + os.Remove(c.RPCPath) + } + } +} + +// Get the config path/ +func ConfigPath() (fileDir, fileName string) { + // Find the configuration directory. + configDirs := configdir.New(serviceVendor, serviceName) + folders := configDirs.QueryFolders(configdir.System) + if len(folders) == 0 { + log.Fatalf("Unable to find config path.") + } + + // Find the file name. + fileName = defaultConfigFile + fileDir = folders[0].Path + if flags.ConfigPath != "" { + fileDir, fileName = filepath.Split(flags.ConfigPath) + } + + // Verify directory exists. + if _, ferr := os.Stat(fileDir); ferr != nil { + err := os.MkdirAll(fileDir, 0755) + if err != nil { + log.Error("Failed to make directory:", err) + } + } + return +} + +// Makes the default config for reading. +func DefaultConfig() *Config { + config := new(Config) + config.Update = &UpdateConfig{ + Owner: "grmrgecko", + Repo: "virtual-vxlan", + } + config.Log = flags.Log + return config +} + +// Read configuration file and return the current config. +func ReadMinimalConfig() *Config { + // Setup default minimal config. + config := DefaultConfig() + + // Find the file name. + fileDir, fileName := ConfigPath() + + // Read the configuration file if it exists. + err := fig.Load(&config.ConfigMinimal, fig.File(fileName), fig.Dirs(fileDir)) + // On error, just print as we want to return a default config. + if err != nil { + log.Debug("Unable to load config file:", err) + } + + // Apply config filters. + config.ApplyFilters(fileDir) + + // Apply any log configurations loaded from file. + config.Log.Apply() + return config +} + +// Read configuration file and return the current config. +func ReadConfig() *Config { + // Setup default config. + config := DefaultConfig() + + // Find the file name. + fileDir, fileName := ConfigPath() + + // Read the configuration file if it exists. + err := fig.Load(config, fig.File(fileName), fig.Dirs(fileDir)) + // On error, just print as we want to return a default config. + if err != nil { + log.Debug("Unable to load config file:", err) + } + + // Apply config filters. + config.ApplyFilters(fileDir) + + // Apply any log configurations loaded from file. + config.Log.Apply() + return config +} + +// Apply the supplied configuration file to this app instance. +func ApplyConfig(config *Config) (err error) { + // Find listeners and interfaces that are permanent + // and not in the config being applied. Remove them + // so that we only have configured permanent entries. + { + var listenersToRemove []*Listener + var interfacesToRemove []*Interface + // Lock to prevent other actions. + app.Net.Lock() + for _, listener := range app.Net.Listeners { + if listener.Permanent { + // The address is the pretty name. + addr := listener.PrettyName() + + // Find listeners in the config that match this address. + var found *ListenerConfig + for _, list := range config.Listeners { + if addr == list.Address && listener.Name == list.Name { + found = &list + break + } + } + + // If we found a listener, check its interfaces. + if found != nil { + // Look for interfaces on this listener which + // are no longer in the config. + listener.net.RLock() + for _, iface := range listener.net.interfaces { + if iface.Permanent { + // Match by both name and VNI, either change + // and we need to remake it. + name := iface.Name() + vni := iface.VNI() + + // Loop to find. + foundIfce := false + for _, ifce := range found.Interfaces { + if ifce.Name == name && ifce.VNI == vni { + foundIfce = true + } + } + // If we didn't find this interface, add to remove list. + if !foundIfce { + interfacesToRemove = append(interfacesToRemove, iface) + } + } + } + listener.net.RUnlock() + } else { + // This listener wasn't found, lets remove it. + listenersToRemove = append(listenersToRemove, listener) + } + } + } + + // Unlock net as we're done looking at lists and we + // need it unlocked to remove items. + app.Net.Unlock() + + // Remove listeners not found. + for _, list := range listenersToRemove { + list.Close() + } + + // Remove interfaces not found. + for _, ifce := range interfacesToRemove { + ifce.Close() + } + } + + // Loop through listeners in the config to add/change the + // configurations of both listeners and their interfaces. + for _, listener := range config.Listeners { + // First check to see if an existing listener is there. + var l *Listener + app.Net.Lock() + for _, list := range app.Net.Listeners { + if list.PrettyName() == listener.Address { + l = list + l.Permanent = true + break + } + } + app.Net.Unlock() + + // If no existing listeners, add a new listener. + if l == nil { + l, err = NewListener(listener.Name, listener.Address, listener.MaxMessageSize, true) + if err != nil { + return fmt.Errorf("failed to start listener: %s %v", listener.Address, err) + } + } else { + // If the listener was already existing, update the max message size. + l.SetMaxMessageSize(listener.MaxMessageSize) + } + + // Loop through interfaces on this listener to add/upate them. + for _, iface := range listener.Interfaces { + // See if this interface is already on the listener. + var i *Interface + l.net.RLock() + for _, ifce := range l.net.interfaces { + if ifce.VNI() == iface.VNI { + i = ifce + i.Permanent = true + } + } + l.net.RUnlock() + + // If this interface isn't existing, add it. + if i == nil { + i, err = NewInterface(iface.Name, iface.VNI, iface.MTU, l, true) + if err != nil { + return fmt.Errorf("failed to make interface: %s %v", iface.Name, err) + } + } else { + // If the interface is existing, update the MTU. + i.SetMTU(iface.MTU) + } + + // Parse the interface's MAC address. + mac, err := net.ParseMAC(iface.MACAddress) + if err != nil { + return fmt.Errorf("failed tp parse MAC: %s %v", iface.MACAddress, err) + } + + // Set the interface's MAC address. + err = i.SetMACAddress(mac) + if err != nil { + return fmt.Errorf("failed to set MAC address %s on interface %s: %v", iface.MACAddress, iface.Name, err) + } + + // Parse the interface's IP addresses CIDRs. + var prefixes []netip.Prefix + for _, addr := range iface.IPAddressCIDRS { + prefix, err := netip.ParsePrefix(addr) + if err != nil { + return fmt.Errorf("failed to parse CIDR: %s %v", addr, err) + } + prefixes = append(prefixes, prefix) + } + + // If IP addresses are set for this interface, set them. + if len(prefixes) != 0 { + err = i.SetIPAddresses(prefixes) + if err != nil { + return fmt.Errorf("failed to set IP addresses on interface: %s %v", iface.Name, err) + } + } + + // Flush the ARP table of any permanent entry. + i.tables.Lock() + found := true + for found { + found = false + for p, ent := range i.tables.arp { + if ent.Permanent { + found = true + i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) + break + } + } + } + i.tables.Unlock() + + // Add permanent ARP entries from the config. + for _, ent := range iface.ARPEntries { + addr, err := netip.ParseAddr(ent.IPAddress) + if err != nil { + return fmt.Errorf("failed to parse IP: %s %v", ent.IPAddress, err) + } + mac, err := net.ParseMAC(ent.MACAddress) + if err != nil { + return fmt.Errorf("failed to parse MAC: %s %v", ent.MACAddress, err) + } + i.AddStaticARPEntry(addr, mac, true) + } + + // Flush the MAC table of any permanent entry. + i.tables.Lock() + found = true + for found { + found = false + for p, ent := range i.tables.mac { + if ent.Permanent { + found = true + i.tables.mac = append(i.tables.mac[:p], i.tables.mac[p+1:]...) + break + } + } + } + i.tables.Unlock() + + // Add permanent entries from this config. + for _, ent := range iface.MACEntries { + mac, err := net.ParseMAC(ent.MACAddress) + if err != nil { + return fmt.Errorf("failed to parse MAC: %s %v", ent.MACAddress, err) + } + dst := net.ParseIP(ent.Destination) + if dst == nil { + return fmt.Errorf("failed to parse destination: %s", ent.Destination) + } + i.AddMACEntry(mac, dst, true) + } + + // Flush the route table of any permanent entry. + i.tables.Lock() + found = true + for found { + found = false + for _, ent := range i.tables.route { + if ent.Permanent { + found = true + i.RemoveStaticRoute(ent.Destination, ent.Gateway) + break + } + } + } + i.tables.Unlock() + + // Add permanent routes from this config. + for _, ent := range iface.StaticRoutes { + destination, err := netip.ParsePrefix(ent.Destination) + if err != nil { + return fmt.Errorf("failed to parse destination prefix %s: %v", ent.Destination, err) + } + gateway, err := netip.ParseAddr(ent.Gateway) + if err != nil { + return fmt.Errorf("failed to parse gateway %s: %v", ent.Gateway, err) + } + i.AddStaticRoute(destination, gateway, ent.Metric, true) + } + } + } + + // No errors occurred, configuration is applied. + return nil +} + +// Take the current application state and save permanent configurations +// to the configuration file. +func SaveConfig() error { + // Start a new configuration file. + config := new(Config) + config.RPCPath = app.grpcServer.RPCPath + config.Update = app.UpdateConfig + config.Log = flags.Log + + // Look the global app config during this process. + app.Net.Lock() + defer app.Net.Unlock() + + // Loop through listeners and add permanent listeners to the config. + for _, listener := range app.Net.Listeners { + // If this listener is permanent, add it. + if listener.Permanent { + // Make a listener config. + listnr := ListenerConfig{ + Address: listener.PrettyName(), + MaxMessageSize: listener.MaxMessageSize(), + Name: listener.Name, + } + + // Loop through interfaces on this listener and add to the config. + listener.net.Lock() + for _, iface := range listener.net.interfaces { + // Only add interface if its permanent. + if iface.Permanent { + // Get the MAC Address. + mac, err := iface.GetMACAddress() + if err != nil { + return err + } + // Get the IP addresses on this interface. + ipAddrs, err := iface.GetIPAddresses() + if err != nil { + return err + } + + // Make the config for this interface. + ifce := InterfaceConfig{ + Name: iface.Name(), + VNI: iface.VNI(), + MTU: iface.MTU(), + MACAddress: mac.String(), + } + + // Add the CIDRs for this interface. + for _, addr := range ipAddrs { + ifce.IPAddressCIDRS = append(ifce.IPAddressCIDRS, addr.String()) + } + + // Get and add permanent ARP entries to the config. + for _, ent := range iface.GetARPEntries() { + if ent.Permanent { + entry := ARPEntryConfig{ + IPAddress: ent.Addr.String(), + MACAddress: ent.MAC.String(), + } + ifce.ARPEntries = append(ifce.ARPEntries, entry) + } + } + + // Get and add permanent MAC enties to the config. + for _, ent := range iface.GetMACEntries() { + if ent.Permanent { + entry := MACEntryConfig{ + MACAddress: ent.MAC.String(), + Destination: ent.Dst.IP.String(), + } + ifce.MACEntries = append(ifce.MACEntries, entry) + } + } + + // Get and add permanent static routes to the config. + for _, ent := range iface.GetStaticRoutes() { + if ent.Permanent { + route := StaticRouteConfig{ + Destination: ent.Destination.String(), + Gateway: ent.Gateway.String(), + Metric: ent.Metric, + } + ifce.StaticRoutes = append(ifce.StaticRoutes, route) + } + } + + // Add this interface config to the listener config. + listnr.Interfaces = append(listnr.Interfaces, ifce) + } + } + listener.net.Unlock() + + // Add this listener to the list of listeners in the config. + config.Listeners = append(config.Listeners, listnr) + } + } + + // Encode YAML data. + data, err := yaml.Marshal(config) + if err != nil { + return err + } + + // Find the file name. + fileDir, fileName := ConfigPath() + + // Write the configuration file. + err = os.WriteFile(filepath.Join(fileDir, fileName), data, 0644) + return err +} + +// Apply log config. +func (l *LogConfig) Apply() { + // Apply level. + switch l.Level { + case "debug": + log.SetLevel(log.DebugLevel) + case "info": + log.SetLevel(log.InfoLevel) + case "warn": + log.SetLevel(log.WarnLevel) + default: + log.SetLevel(log.ErrorLevel) + } + + // Apply type. + switch l.Type { + case "json": + log.SetFormatter(&log.JSONFormatter{}) + default: + log.SetFormatter(&log.TextFormatter{}) + } + + // Change the outputs. + var outputs []io.Writer + for _, output := range l.Outputs { + // If output is console and not running as a service, add stderr and continue. + if output == "console" { + // On windows, you cannot log to stderr when running as an service. + if service.Interactive() && runtime.GOOS == "windows" { + outputs = append(outputs, os.Stderr) + } + continue + } + + // If default-file defined, find the default file. + if output == "default-file" { + var f *os.File + var err error + var logDir, logPath string + logName := fmt.Sprintf("%s.log", serviceName) + + // On *nix, `/var/log/` should be default if writable. + if runtime.GOOS != "windows" { + logDir = "/var/log" + logPath = filepath.Join(logDir, logName) + + // Verify we can write to log file. + f, err = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + } + + // If we could not open the file, then we should try the executable path. + if err != nil || f == nil { + exe, err := os.Executable() + if err != nil { + log.Println("Unable to find an writable log path to save log to.") + continue + } else { + logDir = filepath.Dir(exe) + logPath = filepath.Join(logDir, logName) + + // Verify we can write to log file. + f, err = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + log.Println("Unable to find an writable log path to save log to.") + continue + } else { + f.Close() + } + } + } else { + // Close file. + f.Close() + } + + // Update the config log path. + output = logPath + } + + // Setup lumberjack log rotate for the output, and add to the list. + logFile := &lumberjack.Logger{ + Filename: output, + MaxSize: l.MaxSize, + MaxBackups: l.MaxBackups, + MaxAge: l.MaxAge, + LocalTime: *l.LocalTime, + Compress: *l.Compress, + } + outputs = append(outputs, logFile) + } + + // If there are outputs, set the outputs. + if len(outputs) != 0 { + mw := io.MultiWriter(outputs...) + log.SetOutput(mw) + } +} diff --git a/go.mod b/go.mod index bab563c..9e702d6 100644 --- a/go.mod +++ b/go.mod @@ -3,30 +3,31 @@ module github.com/grmrgecko/virtual-vxlan go 1.23.4 require ( - github.com/alecthomas/kong v1.8.1 + github.com/alecthomas/kong v1.12.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf - github.com/creativeprojects/go-selfupdate v1.4.1 + github.com/creativeprojects/go-selfupdate v1.5.0 github.com/google/gopacket v1.1.19 github.com/hashicorp/go-version v1.7.0 github.com/jedib0t/go-pretty/v6 v6.6.7 github.com/kardianos/service v1.2.2 - github.com/kkyr/fig v0.4.0 + github.com/kkyr/fig v0.5.0 github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 github.com/sirupsen/logrus v1.9.3 - github.com/vishvananda/netlink v1.3.0 - golang.org/x/sys v0.31.0 + github.com/vishvananda/netlink v1.3.1 + golang.org/x/sys v0.33.0 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 - golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb golang.zx2c4.com/wireguard/windows v0.5.3 - google.golang.org/grpc v1.71.0 - google.golang.org/protobuf v1.36.5 + google.golang.org/grpc v1.73.0 + google.golang.org/protobuf v1.36.6 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) require ( - code.gitea.io/sdk/gitea v0.20.0 // indirect - github.com/42wim/httpsig v1.2.2 // indirect - github.com/Masterminds/semver/v3 v3.3.1 // indirect + code.gitea.io/sdk/gitea v0.21.0 // indirect + github.com/42wim/httpsig v1.2.3 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/fatih/color v1.18.0 // indirect @@ -34,25 +35,25 @@ require ( github.com/google/go-github/v30 v30.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.7 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/xanzy/go-gitlab v0.115.0 // indirect - golang.org/x/crypto v0.36.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.37.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.31.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.34.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index a2c65d8..c70af6b 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,21 @@ code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U= +code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= +code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA= github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY= +github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= +github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/kong v1.8.1 h1:6aamvWBE/REnR/BCq10EcozmcpUPc5aGI1lPAWdB0EE= github.com/alecthomas/kong v1.8.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= +github.com/alecthomas/kong v1.12.0 h1:oKd/0fHSdajj5PfGDd3ScvEvpVJf9mT2mb5r9xYadYM= +github.com/alecthomas/kong v1.12.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= @@ -15,6 +23,8 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creativeprojects/go-selfupdate v1.4.1 h1:bgS6RThbKm9LnuRxGQwKOcu6RIOe/rsfWlDkWnkvkrU= github.com/creativeprojects/go-selfupdate v1.4.1/go.mod h1:BRTk+0jZWjqB2Sq0sh9P6MktiieRtoOMpawYDX3+nX0= +github.com/creativeprojects/go-selfupdate v1.5.0 h1:4zuFafc/qGpymx7umexxth2y2lJXoBR49c3uI0Hr+zU= +github.com/creativeprojects/go-selfupdate v1.5.0/go.mod h1:Pewm8hY7Xe1ne7P8irVBAFnXjTkRuxbbkMlBeTdumNQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -35,6 +45,7 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -50,6 +61,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -60,6 +73,8 @@ github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kkyr/fig v0.4.0 h1:4D/g72a8ij1fgRypuIbEoqIT7ukf2URVBtE777/gkbc= github.com/kkyr/fig v0.4.0/go.mod h1:U4Rq/5eUNJ8o5UvOEc9DiXtNf41srOLn2r/BfCyuc58= +github.com/kkyr/fig v0.5.0 h1:D4ym5MYYScOSgqyx1HYQaqFn9dXKzIuSz8N6SZ4rzqM= +github.com/kkyr/fig v0.5.0/go.mod h1:U4Rq/5eUNJ8o5UvOEc9DiXtNf41srOLn2r/BfCyuc58= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -77,6 +92,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -99,6 +116,8 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= +github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= +github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= @@ -108,36 +127,43 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -145,39 +171,51 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= +golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= +golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 443a32a..f1095c6 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ const ( serviceDisplayName = "Virtual VXLAN" serviceVendor = "com.mrgeckosmedia" serviceDescription = "Virtual VXLAN using TUN interfaces" - serviceVersion = "0.2.2" + serviceVersion = "0.2.3" defaultConfigFile = "config.yaml" ) diff --git a/vxlan/vxlan.pb.go b/vxlan/vxlan.pb.go index 1c81e92..8f82a80 100644 --- a/vxlan/vxlan.pb.go +++ b/vxlan/vxlan.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.1 -// protoc v5.29.2 +// protoc v3.21.12 // source: vxlan/vxlan.proto package vxlan diff --git a/vxlan/vxlan_grpc.pb.go b/vxlan/vxlan_grpc.pb.go index 8928ae9..4afb90c 100644 --- a/vxlan/vxlan_grpc.pb.go +++ b/vxlan/vxlan_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.2 +// - protoc v3.21.12 // source: vxlan/vxlan.proto package vxlan