commit fcddaabed2c881008b7e3e20b55d2eab6ca439f1 Author: GRMrGecko Date: Sun Jan 5 22:22:24 2025 -0600 First commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..394355c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.release-env +virtual-vxlan.exe +virtual-vxlan +dist +sysroot/linux* diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..33f9cfa --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,82 @@ +version: 2 + +before: + hooks: + - go mod tidy + - go get golang.org/x/tools/cmd/stringer@latest + - go generate + +builds: + - id: linux-arm64 + main: ./ + goos: + - linux + goarch: + - arm64 + env: + - CGO_ENABLED=1 + - CC=aarch64-linux-gnu-gcc + - CXX=aarch64-linux-gnu-g++ + - CGO_CFLAGS=--sysroot=/sysroot/linux_arm64 + - CGO_LDFLAGS=--sysroot=/sysroot/linux_arm64 -lresolv + - PKG_CONFIG_SYSROOT_DIR=/sysroot/linux_arm64 + - PKG_CONFIG_PATH=/sysroot/linux_arm64/opt/vc/lib/pkgconfig:/sysroot/linux_arm64/usr/lib/aarch64-linux-gnu/pkgconfig:/sysroot/linux_arm64/usr/lib/pkgconfig:/sysroot/linux_arm64/usr/local/lib/pkgconfig + flags: + - -mod=readonly + ldflags: + - -s -w -X main.version={{.Version}} + - id: linux-amd64 + main: ./ + goos: + - linux + goarch: + - amd64 + env: + - CGO_ENABLED=1 + - CC=x86_64-linux-gnu-gcc + - CXX=x86_64-linux-gnu-g++ + - CGO_CFLAGS=--sysroot=/sysroot/linux_amd64 + - CGO_LDFLAGS=--sysroot=/sysroot/linux_amd64 -lresolv + - PKG_CONFIG_SYSROOT_DIR=/sysroot/linux_amd64 + - PKG_CONFIG_PATH=/sysroot/linux_amd64/opt/vc/lib/pkgconfig:/sysroot/linux_amd64/usr/lib/x86_64-linux-gnu/pkgconfig:/sysroot/linux_amd64/usr/lib/pkgconfig:/sysroot/linux_amd64/usr/local/lib/pkgconfig + flags: + - -mod=readonly + ldflags: + - -s -w -X main.version={{.Version}} + - id: windows-x64 + main: ./ + goos: + - windows + goarch: + - amd64 + ldflags: -buildmode=exe + env: + - CGO_ENABLED=1 + - CC=x86_64-w64-mingw32-gcc + - CXX=x86_64-w64-mingw32-g++ + +archives: + - id: standard + format: tar.gz + builds: + - linux-arm64 + - linux-amd64 + name_template: "{{ .ProjectName }}-{{ .Version }}.{{ .Os }}-{{ .Arch }}" + wrap_in_directory: true + - id: windows-x64 + format: zip + builds: + - windows-x64 + name_template: "{{ .ProjectName }}-{{ .Version }}.{{ .Os }}-{{ .Arch }}" + wrap_in_directory: false + files: + - LICENSE.txt + - README.md + - src: wintun/bin/amd64/wintun.dll + dst: wintun.dll + - src: wintun/LICENSE.txt + dst: wintun-LICENSE.txt + + +checksum: + name_template: "checksums.txt" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c0aee19 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +ARG GO_BUILDER_VERSION + +FROM ghcr.io/gythialy/golang-cross:$GO_BUILDER_VERSION + +RUN apt-get update; \ + apt-get --no-install-recommends -y -q install protobuf-compiler; \ + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest; \ + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + +ENV PATH="/root/go/bin:$PATH" diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c11d2f8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2025 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8878a25 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +VERSION ?= $(shell git describe --tags `git rev-list --tags --max-count=1`) +GITREV = $(shell git rev-parse --short HEAD) +BUILDTIME = $(shell date +'%FT%TZ%z') +PACKAGE_NAME := github.com/grmrgecko/virtual-vxlan +GO_BUILDER_VERSION ?= 1.23 + +.PHONY: default +default: build ; + +.PHONY: build-sysroot +build-sysroot: + ./sysroot/build.sh + +.PHONY: build-docker-image +build-docker-image: + docker build -t goreleaser-cross --build-arg GO_BUILDER_VERSION=$(GO_BUILDER_VERSION) . + +.PHONY: deps +deps: + go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + go get -u github.com/git-chglog/git-chglog/cmd/git-chglog + go get -u golang.org/x/tools/cmd/goimports + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + +.PHONY: build +build: + go build + +.PHONY: generate +generate: + go mod tidy + go get golang.org/x/tools/cmd/stringer@latest + go generate + +.PHONY: clean +clean: + rm -rf virtual-vxlan* dist CHANGELOG.md + +.PHONY: changelog +changelog: + git-chglog $(VERSION) > CHANGELOG.md + +.PHONY: snapshot +snapshot: + docker run \ + --rm --privileged \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v $(CURDIR):/go/src/$(PACKAGE_NAME) \ + -v $(CURDIR)/sysroot:/sysroot \ + -w /go/src/$(PACKAGE_NAME) \ + goreleaser-cross:latest \ + --clean --skip=publish --snapshot --verbose + +.PHONY: release +release: changelog + docker run \ + --rm --privileged \ + --env-file .release-env \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v $(CURDIR):/go/src/$(PACKAGE_NAME) \ + -v $(CURDIR)/sysroot:/sysroot \ + -w /go/src/$(PACKAGE_NAME) \ + goreleaser-cross:latest \ + --clean --release-notes=CHANGELOG.md + +.PHONY: lint +lint: + golangci-lint run --fix diff --git a/README.md b/README.md new file mode 100644 index 0000000..c86c42d --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# virtual-vxlan + +Virtual VXLAN is a tool written to allow VXLAN interfaces to be used on Windows. I may also add support for other operating systems as well, but for now this is a Windows and Linux only project due to immediate needs. The tool uses [Wintun](https://www.wintun.net/) and the [WireGuard-go tun drivers](https://github.com/WireGuard/wireguard-go/tree/master/tun) to make virtual interfaces, then it listens for vxlan packets over UDP and does translations between the tun interface and the vxlan listener. + +## Install + +You can either download the latest binary from the releases, or you can build this project. For better performance on Windows, install [Npcap](https://npcap.com/#download). + +## Building + +You can build as follows: + +```bash +make deps +make +``` + +## Running as a service + +This project includes service support built in, simply install and start it as follows: + +```bash +virtual-vxlan service install +virtual-vxlan service start +``` + +## Running from cli + +If you are developing the software, or need more debug output. It may be worth running from the cli. + +```bash +virtual-vxlan server --log-level=debug +``` + +## Config + +The configuration is mainly managed by the service itself, however you may set manual configurations according to the `config.go` file. + +## Usage + +The cli has extensive help available via the following: + +```bash +virtual-vxlan --help +``` + +Basic setup of a vxlan service is as follows. + +- Start the service and/or server. + +- Add vxlan listener: +```bash +virtual-vxlan listener vxlan add --address='10.0.0.2:4789' --permanent +``` + +Note: It is important to use the IP adddress of an interface on the system to allow the interface to be put into promiscuous mode, which is required as otherwise hardware vxlan filtering will block the packets from being received and sent. This is a known issue on Windows, however it has not been tested on other operating systems. + +- Add tun interface to the vxlan listener: +```bash +virtual-vxlan listener vxlan interface vxlan20 add --vni=20 --permanent +``` + +- Set an IP address on the interface: +```bash +virtual-vxlan listener vxlan interface vxlan20 set-ip-addresses --ip-address=192.168.30.0/24 +``` + +- Set a default destination for vxlan packets: +```bash +virtual-vxlan listener vxlan interface vxlan20 add-mac-entry --mac="00:00:00:00:00:00" --destination="10.0.0.3" --permanent +``` + +- Save the configuration: +```bash +virtual-vxlan config save +``` diff --git a/config.go b/config.go new file mode 100644 index 0000000..e411892 --- /dev/null +++ b/config.go @@ -0,0 +1,512 @@ +package main + +import ( + "fmt" + "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:"level" yaml:"level" enum:"json,console" default:"console"` +} + +// 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"` +} + +// 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"` +} + +// Applies common filters to the read configuration. +func (c *Config) ApplyFilters() { + // If the RPC path isn't set, set it to temp dir. + if c.RPCPath == "" { + c.RPCPath = filepath.Join(os.TempDir(), "virtual-vxlan.sock") + } + // 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) + } + 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() + + // 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() + + // 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) + } + } + } + + // 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) + } + } + + // 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() + + // 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) + } + } + + // Write the configuration file. + err = os.WriteFile(filepath.Join(fileDir, fileName), data, 0644) + return err +} + +func (l *LogConfig) Apply() { + 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) + } + switch l.Type { + case "json": + log.SetFormatter(&log.JSONFormatter{}) + default: + log.SetFormatter(&log.TextFormatter{}) + } +} diff --git a/config_cmd.go b/config_cmd.go new file mode 100644 index 0000000..3f60f23 --- /dev/null +++ b/config_cmd.go @@ -0,0 +1,66 @@ +package main + +import ( + "context" + "fmt" + "time" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" +) + +// The command for saving configuration. +type ConfigSaveCmd struct { +} + +func (a *ConfigSaveCmd) Run() (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to save configuration. + _, err = c.SaveConfig(ctx, &pb.Empty{}) + if err != nil { + return + } + + fmt.Println("Configuration saved") + return +} + +type ConfigReloadCmd struct { +} + +func (a *ConfigReloadCmd) Run() (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to reload the configuration. + _, err = c.ReloadConfig(ctx, &pb.Empty{}) + if err != nil { + return + } + + fmt.Println("Configuration reloaded") + return +} + +// Commands for managing the configuration. +type ConfigCmd struct { + Save ConfigSaveCmd `cmd:""` + Reload ConfigReloadCmd `cmd:""` +} diff --git a/config_grpc.go b/config_grpc.go new file mode 100644 index 0000000..f71f849 --- /dev/null +++ b/config_grpc.go @@ -0,0 +1,30 @@ +package main + +import ( + "context" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" + log "github.com/sirupsen/logrus" +) + +// Save configuration to yaml file. +func (s *GRPCServer) SaveConfig(ctx context.Context, in *pb.Empty) (*pb.Empty, error) { + log.Println("Saving configurations.") + err := SaveConfig() + if err != nil { + return nil, err + } + return new(pb.Empty), nil +} + +// Reload the configuration from the yaml file. +func (s *GRPCServer) ReloadConfig(ctx context.Context, in *pb.Empty) (*pb.Empty, error) { + log.Println("Reloading configurations.") + config := ReadConfig() + err := ApplyConfig(config) + if err != nil { + log.Println(err) + return nil, err + } + return new(pb.Empty), nil +} diff --git a/devicestate_string.go b/devicestate_string.go new file mode 100644 index 0000000..b34cd1d --- /dev/null +++ b/devicestate_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type deviceState -trimprefix=deviceState"; DO NOT EDIT. + +package main + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[deviceStateDown-0] + _ = x[deviceStateUp-1] + _ = x[deviceStateClosed-2] +} + +const _deviceState_name = "DownUpClosed" + +var _deviceState_index = [...]uint8{0, 4, 6, 12} + +func (i deviceState) String() string { + if i >= deviceState(len(_deviceState_index)-1) { + return "deviceState(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _deviceState_name[_deviceState_index[i]:_deviceState_index[i+1]] +} diff --git a/flags.go b/flags.go new file mode 100644 index 0000000..7bde0e0 --- /dev/null +++ b/flags.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/alecthomas/kong" +) + +type VersionFlag bool + +func (v VersionFlag) Decode(ctx *kong.DecodeContext) error { return nil } +func (v VersionFlag) IsBool() bool { return true } +func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error { + fmt.Println(serviceName + ": " + serviceVersion) + os.Exit(0) + return nil +} + +// Flags supplied to cli. +type Flags struct { + Version VersionFlag `name:"version" help:"Print version information and quit"` + ConfigPath string `help:"The path to the config file" optional:"" type:"existingfile"` + Log *LogConfig `embed:"" prefix:"log-"` + Server ServerCmd `cmd:"" help:"Run the Virtual VXLAN service"` + Service ServiceCmd `cmd:"" help:"Manage the Virtual VXLAN service"` + Listener ListenerCmd `cmd:"" help:"Manage listeners"` + Config ConfigCmd `cmd:"" help:"Manage configuration"` + Update UpdateCmd `cmd:"" help:"Check for updates and apply"` +} + +var flags *Flags + +// Parse the supplied flags. +func ParseFlags() *kong.Context { + flags = new(Flags) + + ctx := kong.Parse(flags, + kong.Name(serviceName), + kong.Description(serviceDescription), + kong.UsageOnError(), + kong.ConfigureHelp(kong.HelpOptions{ + Compact: true, + }), + kong.Vars{ + "serviceActions": strings.Join(ServiceAction, ","), + }, + ) + return ctx +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1c70a10 --- /dev/null +++ b/go.mod @@ -0,0 +1,59 @@ +module github.com/grmrgecko/virtual-vxlan + +go 1.23.4 + +require ( + github.com/alecthomas/kong v1.6.0 + github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf + github.com/creativeprojects/go-selfupdate v1.4.0 + github.com/google/gopacket v1.1.19 + github.com/hashicorp/go-version v1.7.0 + github.com/jedib0t/go-pretty/v6 v6.6.5 + github.com/kardianos/service v1.2.2 + github.com/kkyr/fig v0.4.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.29.0 + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 + golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + golang.zx2c4.com/wireguard/windows v0.5.3 + google.golang.org/grpc v1.69.2 + google.golang.org/protobuf v1.36.1 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + code.gitea.io/sdk/gitea v0.19.0 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // 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 + github.com/go-fed/httpsig v1.1.0 // indirect + 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/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/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/stretchr/testify v1.10.0 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect + github.com/vishvananda/netns v0.0.4 // indirect + github.com/xanzy/go-gitlab v0.112.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.32.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/term v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.28.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..234842a --- /dev/null +++ b/go.sum @@ -0,0 +1,178 @@ +code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= +code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= +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/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.6.0 h1:mwOzbdMR7uv2vul9J0FU3GYxE7ls/iX1ieMg5WIM6gE= +github.com/alecthomas/kong v1.6.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= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creativeprojects/go-selfupdate v1.4.0 h1:4ePPd2CPCNl/YoPXeVxpuBLDUZh8rMEKP5ac+1Y/r5c= +github.com/creativeprojects/go-selfupdate v1.4.0/go.mod h1:oPG7LmzEmS6OxfqEm620k5VKxP45xFZNKMkp4V5qqUY= +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= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= +github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= +github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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-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= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +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-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= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3NgSqAo= +github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= +github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= +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/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= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +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/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= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM= +github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY= +github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= +github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw= +github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +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.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +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.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +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/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-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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/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/grpc.go b/grpc.go new file mode 100644 index 0000000..cf95367 --- /dev/null +++ b/grpc.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + "net" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// Allows go generate to compile the protobuf to golang. +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./vxlan/vxlan.proto + +// GRPC server structure. +type GRPCServer struct { + pb.UnimplementedVxlanServer + RPCPath string + server *grpc.Server +} + +// Start serving GRPC requests. +func (s *GRPCServer) Serve(li net.Listener) { + err := s.server.Serve(li) + if err != nil { + log.Errorf("Error serving grpc: %v", err) + } +} + +// Stop GRPC server. +func (s *GRPCServer) Close() { + s.server.Stop() +} + +// Start GRPC server. +func NewGRPCServer(rpcPath string) (s *GRPCServer, err error) { + // Verify another server doesn't exist. + if app.grpcServer != nil { + return nil, fmt.Errorf("grpc server is already running") + } + + // Connect to RPC path. + li, err := net.Listen("unix", rpcPath) + if err != nil { + return nil, fmt.Errorf("failed to listen on socket: %v", err) + } + + // Setup server. + s = new(GRPCServer) + s.server = grpc.NewServer() + + // Register the vxlan service to this server. + pb.RegisterVxlanServer(s.server, s) + + // Update the global app gRPC server. + app.grpcServer = s + + // Start serving requests. + go s.Serve(li) + return s, nil +} + +// Start a connection to the gRPC Server. +func NewGRPCClient() (c pb.VxlanClient, conn *grpc.ClientConn, err error) { + // Read the minimal config. + config := ReadMinimalConfig() + + // Start an gRPC client connection to the unix socket. + conn, err = grpc.NewClient(fmt.Sprintf("unix:%s", config.RPCPath), grpc.WithTransportCredentials(insecure.NewCredentials())) + + // If connection is successful, provide client to the vxlan service. + if err == nil { + c = pb.NewVxlanClient(conn) + } + return +} diff --git a/interface.go b/interface.go new file mode 100644 index 0000000..e4854d1 --- /dev/null +++ b/interface.go @@ -0,0 +1,1154 @@ +package main + +import ( + "bytes" + "errors" + "fmt" + "math/rand" + "net" + "net/netip" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/grmrgecko/virtual-vxlan/tun" + log "github.com/sirupsen/logrus" +) + +// ARP Table Entry. +type ArpEntry struct { + Addr netip.Addr + MAC net.HardwareAddr + Expires time.Time + Permanent bool + Updating bool +} + +// MAC Table Entry. +type MacEntry struct { + MAC net.HardwareAddr + Dst *net.UDPAddr + Permanent bool +} + +// The vxlan interface containing the tun device. +type Interface struct { + name string + vni uint32 + + state struct { + state atomic.Uint32 + stopping sync.WaitGroup + sync.Mutex + } + + tun struct { + device tun.Device + mtu atomic.Int32 + mtuc chan int + mac net.HardwareAddr + addresses []netip.Prefix + sync.RWMutex + } + + tables struct { + arp []ArpEntry + arpEvent map[netip.Addr][]chan net.HardwareAddr + mac []MacEntry + sync.RWMutex + } + + Permanent bool + l *Listener + closed chan struct{} + log *log.Entry +} + +// Create an interface for the vxlan VNI. +func NewInterface(name string, vni uint32, mtu int, l *Listener, perm bool) (i *Interface, err error) { + // Verify we have an listener, and lock it. + if l == nil { + return nil, fmt.Errorf("we need a listener to attach to") + } + if !l.Permanent && perm { + return nil, fmt.Errorf("cannot add permanent interface to non-permanent listener") + } + l.net.Lock() + defer l.net.Unlock() + + // Check that an interface doesn't already exist with this VNI. + for _, ifce := range l.net.interfaces { + if ifce.vni == vni { + return nil, fmt.Errorf("existing vni %d interface on listener %s", vni, l.PrettyName()) + } + } + + // Verify an interface on the machine doesn't already have the requested name. + ifaces, err := net.Interfaces() + if err != nil { + return + } + for _, iface := range ifaces { + if iface.Name == name { + return nil, fmt.Errorf("existing interface with name %s", name) + } + } + + // If MTU is not provided, use the listener's MTU minus 50 bytes for vxlan. + if mtu <= 1 { + mtu = l.net.maxMessageSize - 50 + } + + // Create the tunnel interface. + tun, err := tun.CreateTUN(name, mtu) + if err != nil { + return nil, fmt.Errorf("failed to create TUN device: %v", err) + } + + // Verify the real name from the interface in-case the OS decided + // to change it from the request. + realName, err := tun.Name() + if err != nil { + return nil, fmt.Errorf("failed to get device name: %v", err) + } + + // Verify the MTU incase the OS changed from requested MTU. + mtu, err = tun.MTU() + if err != nil { + return nil, fmt.Errorf("failed to get device mtu: %v", err) + } + + // Setup the interface structure. + i = new(Interface) + i.name = realName + i.vni = vni + i.state.state.Store(uint32(deviceStateDown)) + i.closed = make(chan struct{}) + i.tun.device = tun + i.tun.mtu.Store(int32(mtu)) + i.tun.mtuc = make(chan int) + i.tun.mac = generateRandomMAC() + i.tables.arpEvent = make(map[netip.Addr][]chan net.HardwareAddr) + i.l = l + i.log = log.WithFields(log.Fields{ + "listener": l.PrettyName(), + "device": name, + "vni": vni, + }) + i.Permanent = perm + l.net.interfaces = append(l.net.interfaces, i) + + // Start the task queues. + go i.runARPExpiryJob() + go i.eventReader() + go i.packetReader() + + // Inform that the interface has started. + i.log.Print("Interface started.") + + return +} + +// The state of the tun device. +type deviceState uint32 + +//go:generate go run golang.org/x/tools/cmd/stringer -type deviceState -trimprefix=deviceState +const ( + deviceStateDown deviceState = iota + deviceStateUp + deviceStateClosed +) + +// Gets the current device state. +func (i *Interface) DeviceState() deviceState { + return deviceState(i.state.state.Load()) +} + +// Is the device closed? +func (i *Interface) IsClosed() bool { + return i.DeviceState() == deviceStateClosed +} + +// Is the device up? +func (i *Interface) IsUp() bool { + return i.DeviceState() == deviceStateUp +} + +// Change the device state. +func (i *Interface) changeState(want deviceState) { + i.state.Lock() + defer i.state.Unlock() + old := i.DeviceState() + if old == deviceStateClosed { + // once closed, always closed + i.log.Debugf("Interface closed, ignored requested state %v", want) + return + } + switch want { + case old: + return + case deviceStateUp: + i.state.state.Store(uint32(deviceStateUp)) + fallthrough // up failed; bring the device all the way back down + case deviceStateDown: + i.state.state.Store(uint32(deviceStateDown)) + } + i.log.Debugf("Interface state was %v, requested %v, now %v", old, want, i.DeviceState()) +} + +// Bring the internal device state to up. +func (i *Interface) Up() { + i.changeState(deviceStateUp) +} + +// Bring the internal device state to down. +func (i *Interface) Down() { + i.changeState(deviceStateDown) +} + +// Set the interface MTU. +func (i *Interface) SetMTU(mtu int) error { + // If MTU is not provided, use the listener's MTU minus 50 bytes for vxlan. + if mtu <= 1 { + mtu = i.l.net.maxMessageSize - 50 + } + return i.tun.device.SetMTU(mtu) +} + +// Get the interface MTU. +func (i *Interface) MTU() int { + return int(i.tun.mtu.Load()) +} + +// Get the interface name. +func (i *Interface) Name() string { + return i.name +} + +// Get the VNI assigned for this interface. +func (i *Interface) VNI() uint32 { + return i.vni +} + +// Close this interface. +func (i *Interface) Close() (err error) { + i.state.Lock() + defer i.state.Unlock() + if i.IsClosed() { + return + } + i.state.state.Store(uint32(deviceStateClosed)) + i.log.Debug("Interface is closing.") + + err = i.tun.device.Close() + i.state.stopping.Wait() + + // Remove self from listener. + netc := &i.l.net + netc.Lock() + defer netc.Unlock() + for p, iface := range netc.interfaces { + if iface == i { + netc.interfaces = append(netc.interfaces[:p], netc.interfaces[p+1:]...) + break + } + } + close(i.closed) + i.log.Print("Interface closed.") + return +} + +// Set the MAC address for this interface. +func (i *Interface) SetMACAddress(mac net.HardwareAddr) (err error) { + i.tun.Lock() + i.tun.mac = mac + i.tun.Unlock() + return +} + +// Get the MAC address for this interface. +func (i *Interface) GetMACAddress() (net.HardwareAddr, error) { + i.tun.RLock() + defer i.tun.RUnlock() + return i.tun.mac, nil +} + +// Set IP addresses on this interface. +func (i *Interface) SetIPAddresses(addresses []netip.Prefix) (err error) { + i.tun.Lock() + defer i.tun.Unlock() + err = i.tun.device.SetIPAddresses(addresses) + if err != nil { + return + } + i.tun.addresses = addresses + return +} + +// Get IP adddreses on this interface. +func (i *Interface) GetIPAddresses() ([]netip.Prefix, error) { + return i.tun.device.GetIPAddresses() +} + +// Add an entry to the MAC table. +func (i *Interface) AddMACEntry(mac net.HardwareAddr, dst net.IP, perm bool) error { + // Lock tables to prevent concurrent requests. + i.tables.Lock() + defer i.tables.Unlock() + + // Verify this entry doesn't already exist. + for _, ent := range i.tables.mac { + if bytes.Equal(ent.MAC, mac) && ent.Dst.IP.Equal(dst) { + return fmt.Errorf("entry already exists for %v via %v", mac, dst) + } + } + + // Get the UDP address for the provided IP. + addr, _ := netip.AddrFromSlice(dst) + udpAddr := net.UDPAddrFromAddrPort(netip.AddrPortFrom(addr, uint16(i.l.net.addr.Port))) + + // Add entry to MAC table. + entry := MacEntry{ + MAC: mac, + Dst: udpAddr, + Permanent: perm, + } + i.tables.mac = append(i.tables.mac, entry) + return nil +} + +// Remove an individual MAC table entry. +func (i *Interface) RemoveMACEntry(mac net.HardwareAddr, dst net.IP) error { + // Lock table during modification. + i.tables.Lock() + defer i.tables.Unlock() + + // Find matching entry. + found := -1 + for i, ent := range i.tables.mac { + if bytes.Equal(ent.MAC, mac) && ent.Dst.IP.Equal(dst) { + found = i + break + } + } + + // If not found return error. + if found == -1 { + return fmt.Errorf("unable to find matching MAC entry") + } + + // If found, remove it. + i.tables.mac = append(i.tables.mac[:found], i.tables.mac[found+1:]...) + return nil +} + +// Returns the MAC to destination table. +func (i *Interface) GetMACEntries() []MacEntry { + i.tables.RLock() + defer i.tables.RUnlock() + return i.tables.mac +} + +// Clears the MAC to UDP Address association table. +func (i *Interface) FlushMACTable() { + i.tables.Lock() + i.tables.mac = nil + i.tables.Unlock() +} + +// Find a destination UDP address for a MAC address. +func (i *Interface) GetDestinationFor(mac net.HardwareAddr) *net.UDPAddr { + // Lock tables to prevent concurrent requests. + i.tables.RLock() + defer i.tables.RUnlock() + + // Keep track of both controller and direct matches. + var controllers []*net.UDPAddr + var matches []*net.UDPAddr + + // Find matches. + for _, ent := range i.tables.mac { + if bytes.Equal(ent.MAC, app.ControllerMac) { + controllers = append(controllers, ent.Dst) + } else if bytes.Equal(ent.MAC, mac) { + matches = append(matches, ent.Dst) + } + } + + // If we have an direct match, return a random entry. + if len(matches) != 0 { + return matches[rand.Intn(len(matches))] + } + + // If we have no direct matches, but controllers, return a random controller. + if len(controllers) != 0 { + return controllers[rand.Intn(len(controllers))] + } + + // If we found no matches at all, return nil. + return nil +} + +// Add an ARP entry that is static. +func (i *Interface) AddStaticARPEntry(addr netip.Addr, mac net.HardwareAddr, perm bool) { + // Prevent concurrent table modifications. + i.tables.Lock() + defer i.tables.Unlock() + + // Keep track on if we updated an existing entry. + updated := false + + // Check existing arp entires for this address. + for p, ent := range i.tables.arp { + // If this address is this entry, update or remove based on MAC. + if ent.Addr == addr { + // If MAC matches, update it. Otherwise, remove + // tne ARP entry as we're adding a new one. + if bytes.Equal(ent.MAC, mac) { + updated = true + i.tables.arp[p].Expires = time.Time{} + i.tables.arp[p].Updating = false + i.tables.arp[p].Permanent = perm + } else { + i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) + } + break + } + } + + // If we didn't update an existing entry, we should add a new one. + if !updated { + newMac := make(net.HardwareAddr, 6) + copy(newMac, mac) + entry := ArpEntry{ + Addr: addr, + MAC: newMac, + Updating: false, + Permanent: perm, + } + i.tables.arp = append(i.tables.arp, entry) + } +} + +// Remove an individual MAC table entry. +func (i *Interface) RemoveARPEntry(addr netip.Addr) error { + // Lock table during modification. + i.tables.Lock() + defer i.tables.Unlock() + + // Find matching entry. + found := -1 + for i, ent := range i.tables.arp { + if ent.Addr == addr { + found = i + break + } + } + + // If not found return error. + if found == -1 { + return fmt.Errorf("unable to find matching ARP entry") + } + + // If found, remove it. + i.tables.arp = append(i.tables.arp[:found], i.tables.arp[found+1:]...) + return nil +} + +// Get ARP entries. +func (i *Interface) GetARPEntries() []ArpEntry { + i.tables.RLock() + defer i.tables.RUnlock() + return i.tables.arp +} + +// Add ARP entry, or update expiry on existing entry. +func (i *Interface) AddOrUpdateARP(addr netip.Addr, mac net.HardwareAddr) { + // Prevent concurrent table modifications. + i.tables.Lock() + defer i.tables.Unlock() + + // Keep track on if we updated an existing entry. + updated := false + + // Check existing arp entires for this address. + for p, ent := range i.tables.arp { + // If this address is this entry, update or remove based on MAC. + if ent.Addr == addr { + // If MAC matches, update it. Otherwise, remove + // tne ARP entry as we're adding a new one. + if bytes.Equal(ent.MAC, mac) { + updated = true + i.tables.arp[p].Expires = time.Now().Add(60 * time.Second) + i.tables.arp[p].Updating = false + } else { + i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) + } + break + } + } + + // If we didn't update an existing entry, we should add a new one. + if !updated { + newMac := make(net.HardwareAddr, 6) + copy(newMac, mac) + entry := ArpEntry{ + Addr: addr, + MAC: newMac, + Expires: time.Now().Add(60 * time.Second), + Updating: false, + Permanent: false, + } + i.tables.arp = append(i.tables.arp, entry) + } +} + +// In the event of finding an expired MAC entry, we should +// try updating the entry to ensure its not changed. +func (i *Interface) updateARPFor(addr netip.Addr, brdMac net.HardwareAddr, ifceAddr netip.Addr) { + // Lock tables to make update channel. + i.tables.Lock() + + // As we did not find an existing entry, we need to make an ARP request. + // Start a channel to receive the ARP event back with the MAC. + c := make(chan net.HardwareAddr) + // Add to the event list. + i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr], c) + // When this function is done, ensure the event channel is removed. + defer func() { + i.tables.Lock() + for p, ch := range i.tables.arpEvent[addr] { + if ch == c { + i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr][:p], i.tables.arpEvent[addr][p+1:]...) + break + } + } + i.tables.Unlock() + }() + + // Unlock the tables to allow ARP replies to add to the tables. + i.tables.Unlock() + + // Get an destination UDP address for the ARP request. + dst := i.GetDestinationFor(brdMac) + if dst == nil { + return + } + + // Make Ethernet layer for packet. + eth := layers.Ethernet{ + SrcMAC: i.tun.mac, + DstMAC: brdMac, + EthernetType: layers.EthernetTypeARP, + } + + // Make ARP layer for packet. + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + HwAddressSize: 6, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(i.tun.mac), + SourceProtAddress: ifceAddr.AsSlice(), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + DstProtAddress: addr.AsSlice(), + } + // Size and protocol type differs on IPv6 vs IPv4. + if addr.Is4() { + arp.Protocol = layers.EthernetTypeIPv4 + arp.ProtAddressSize = 4 + } else { + arp.Protocol = layers.EthernetTypeIPv6 + arp.ProtAddressSize = 16 + } + + // We give 3 ARP request tries, otherwise we timeout. + tries := 0 + for tries < 3 { + tries += 1 + // Try sending packet. + i.SendLayers(dst, ð, &arp) + select { + // If we receive the response, return the value. + case <-c: + return + // If we timeout, after 3 seconds, then continue. + case <-time.After(3 * time.Second): + i.log.Debugf("timeout on ARP request for %s with tries %d", addr.String(), tries) + continue + } + } + // If all tries are done, the addres is expired. + i.log.Debugf("MAC address for %s has expired", addr.String()) + + // Lock tables and find the entry to remove. + i.tables.Lock() + for p, ent := range i.tables.arp { + // If this is the right entry, remove it. + if ent.Addr == addr { + i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) + break + } + } + i.tables.Unlock() +} + +// Find the MAC address for an IP address from the ARP table. +// If an entry doesn't exist, we will attempt to request it. +func (i *Interface) GetMacFor(addr netip.Addr) (net.HardwareAddr, error) { + // Lots of math depending on is4. + is4 := addr.Is4() + + // Multicast traffic should be destined to a multicast MAC address. + if addr.IsMulticast() { + if is4 { + // Get the lower 23 bits of the IPv4 address. + ip := addr.As4() + lower23 := uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) + lower23 &= 0x7FFFFF // Mask to 23 bits + + // Construct the MAC address. + mac := net.HardwareAddr{ + 0x01, 0x00, 0x5E, + byte((lower23 >> 16) & 0xFF), + byte((lower23 >> 8) & 0xFF), + byte(lower23 & 0xFF), + } + return mac, nil + } else { + // Construct MAC Address using the lower 32 bits of the IPv6 address. + ip := addr.As16() + mac := net.HardwareAddr{ + 0x33, 0x33, + ip[12], + ip[13], + ip[14], + ip[15], + } + return mac, nil + } + } + + // Find an IP address on this interface that matches the IP we're pinging. + i.tun.RLock() + var ifcePrefix netip.Prefix + var ifceAddr netip.Addr + for _, prefix := range i.tun.addresses { + // If this prefix is an exact match, this is the IP we use. + // Otherwise, we will accept the IP of the same type. + if prefix.Contains(addr) { + ifcePrefix = prefix + ifceAddr = prefix.Addr() + break + } else if prefix.Addr().Is4() == is4 { + ifcePrefix = prefix + ifceAddr = prefix.Addr() + } + } + i.tun.RUnlock() + + // Set broadcast MAC. + brdMac := net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + + // If this is IPv4, determine if the address provided is broadcast + // and return broadcast mac. + if is4 && ifcePrefix.Contains(ifceAddr) { + // Get the host bits to mask IP to broadcast. + hostBits := 32 - ifcePrefix.Bits() + + // Set all host bits to 1 to calculate the broadcast address. + broadcastAddr := ifceAddr.As4() + for i := 0; i < hostBits; i++ { + broadcastAddr[3-i/8] |= byte(1 << (i % 8)) + } + + // If the provided address is the broadcast, return the broadcast MAC. + if addr == netip.AddrFrom4(broadcastAddr) { + return brdMac, nil + } + } + + // Lock tables, no concurrent requests. + i.tables.Lock() + + // Check arp table for existing entry. + for _, ent := range i.tables.arp { + // If we found an existing entry, return the value. + if ent.Addr == addr { + defer i.tables.Unlock() + // If we're not already updating, check if this entry is expired. + if !ent.Updating && !ent.Expires.IsZero() { + now := time.Now() + // If expired, attempt to update MAC address in background. + if now.After(ent.Expires) { + ent.Updating = true + go i.updateARPFor(addr, brdMac, ifceAddr) + } + } + return ent.MAC, nil + } + } + + // As we did not find an existing entry, we need to make an ARP request. + // Start a channel to receive the ARP event back with the MAC. + c := make(chan net.HardwareAddr) + // Add to the event list. + i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr], c) + // When this function is done, ensure the event channel is removed. + defer func() { + i.tables.Lock() + for p, ch := range i.tables.arpEvent[addr] { + if ch == c { + i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr][:p], i.tables.arpEvent[addr][p+1:]...) + break + } + } + i.tables.Unlock() + }() + + // Unlock the tables to allow ARP replies to add to the tables. + i.tables.Unlock() + + // Get an destination UDP address for the ARP request. + dst := i.GetDestinationFor(brdMac) + if dst == nil { + return nil, fmt.Errorf("no destination to send ARP to") + } + + // Make Ethernet layer for packet. + eth := layers.Ethernet{ + SrcMAC: i.tun.mac, + DstMAC: brdMac, + EthernetType: layers.EthernetTypeARP, + } + + // Make ARP layer for packet. + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + HwAddressSize: 6, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(i.tun.mac), + SourceProtAddress: ifceAddr.AsSlice(), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + DstProtAddress: addr.AsSlice(), + } + // Size and protocol type differs on IPv6 vs IPv4. + if is4 { + arp.Protocol = layers.EthernetTypeIPv4 + arp.ProtAddressSize = 4 + } else { + arp.Protocol = layers.EthernetTypeIPv6 + arp.ProtAddressSize = 16 + } + + // We give 3 ARP request tries, otherwise we timeout. + tries := 0 + for tries < 3 { + tries += 1 + // Try sending packet. + i.SendLayers(dst, ð, &arp) + select { + // If we receive the response, return the value. + case mac := <-c: + return mac, nil + // If we timeout, after 3 seconds, then continue. + case <-time.After(3 * time.Second): + i.log.Debugf("timeout on ARP request for %s with tries %d", addr.String(), tries) + continue + } + } + // If all tries are done, return error. + return nil, fmt.Errorf("unable to get MAC address for %s", addr.String()) +} + +// A job to check for expired arp entries and remove them from the arp table. +func (i *Interface) runARPExpiryJob() { + // Run job every minute. + ticker := time.NewTicker(1 * time.Minute) + for { + // Run the job queue if this interface doesn't close. + select { + case <-ticker.C: + // Lock tables for ARP operations. + i.tables.Lock() + // Get current time to compare against expiry date. + now := time.Now() + // Start with a found entry so the loop runs first. + foundEntry := true + for foundEntry { + // Set to no found entry, so we stop the loop if no entry is found. + foundEntry = false + // Scan arp table for expired entries. + for p, ent := range i.tables.arp { + // If this entry is expired and past 2 minute grace, remove it and set that we found an entry. + if !ent.Expires.IsZero() && now.Before(ent.Expires.Add(120*time.Second)) { + foundEntry = true + i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) + break + } + } + } + // No further expired entries found, unlock the table. + i.tables.Unlock() + case <-i.closed: + return + } + } +} + +// Clears the arp table. +func (i *Interface) FlushARPTable() { + i.tables.Lock() + i.tables.arp = nil + i.tables.Unlock() +} + +// Handle an ARP packet received. +func (i *Interface) HandleARP(arp layers.ARP) { + // If type isn't ethernet, we don't care. + if arp.AddrType != layers.LinkTypeEthernet { + return + } + // If ARP request, we should verify its for our IP. + if arp.Operation == layers.ARPRequest { + // Parse the destination address. + addr, ok := netip.AddrFromSlice(arp.DstProtAddress) + if !ok { + return + } + // Find the address that matches the requested address. + i.tun.RLock() + defer i.tun.RUnlock() + for _, prefix := range i.tun.addresses { + // If the requested address is this one, we should reply. + if prefix.Addr() == addr { + // Get the MAC address and destination to send reply to. + srcMac := net.HardwareAddr(arp.SourceHwAddress) + dst := i.GetDestinationFor(srcMac) + if dst == nil { + return + } + + // Parse the source address. + srcAddr, ok := netip.AddrFromSlice(arp.SourceProtAddress) + if !ok { + return + } + + // Add the ARP entry for the source. + i.AddOrUpdateARP(srcAddr, srcMac) + + // Make an ethernet layer for sending ARP reply. + eth := layers.Ethernet{ + SrcMAC: i.tun.mac, + DstMAC: srcMac, + EthernetType: layers.EthernetTypeARP, + } + + // Make the ARP reply layer. + arpReply := layers.ARP{ + AddrType: arp.AddrType, + Protocol: arp.Protocol, + HwAddressSize: arp.HwAddressSize, + ProtAddressSize: arp.ProtAddressSize, + Operation: layers.ARPReply, + SourceHwAddress: []byte(i.tun.mac), + SourceProtAddress: addr.AsSlice(), + DstHwAddress: srcMac, + DstProtAddress: srcAddr.AsSlice(), + } + + // Send the ARP reply. + i.SendLayers(dst, ð, &arpReply) + } + } + // On ARP reply, we should verify its to us and update table. + } else if arp.Operation == layers.ARPReply { + // If not destined to us, we should stop here. + if !bytes.Equal(i.tun.mac, arp.DstHwAddress) { + i.log.Debugf("Mac doesn't match our MAC: %s", net.HardwareAddr(arp.DstHwAddress)) + return + } + + // Parse the source. + srcMac := net.HardwareAddr(arp.SourceHwAddress) + srcAddr, ok := netip.AddrFromSlice(arp.SourceProtAddress) + if !ok { + return + } + + // Add the source of the ARP reply to the ARP table. + i.AddOrUpdateARP(srcAddr, srcMac) + + // Send event to any ARP reply event listeners. + i.tables.RLock() + chs, ok := i.tables.arpEvent[srcAddr] + if ok { + for _, c := range chs { + c <- srcMac + } + } + i.tables.RUnlock() + } +} + +// Process IPv4 packet. +func (i *Interface) HandleIPv4(eth layers.Ethernet, ip4 layers.IPv4) { + // Verify the destination is us. + if !bytes.Equal(eth.DstMAC, i.tun.mac) { + return + } + + // Pass packet to the interface. + data := append(ip4.Contents, ip4.Payload...) + i.Write(data) +} + +// Process IPv6 packet. +func (i *Interface) HandleIPv6(eth layers.Ethernet, ip6 layers.IPv6) { + // Verify the destination is us. + if !bytes.Equal(eth.DstMAC, i.tun.mac) { + return + } + + // Pass packet to the interface. + data := append(ip6.Contents, ip6.Payload...) + i.Write(data) +} + +// Send a packet to a destination with ethernet layers and bytes to append to encoded packet. +// The packet will be encapsulated in vxlan tunnel. +func (i *Interface) SendLayers(dst *net.UDPAddr, layersToSend ...gopacket.SerializableLayer) { + // Make the VXLAN layer with our VNI. + vxlan := layers.VXLAN{ + ValidIDFlag: true, + VNI: i.vni, + } + + // Setup buffer for encoding the packet. + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + + // Encode packet with the vxlan and other provided layers. + err := gopacket.SerializeLayers(buf, opts, append([]gopacket.SerializableLayer{&vxlan}, layersToSend...)...) + if err != nil { + i.log.Errorf("failed to encode layers: %v", err) + } + + // Send packet to destination. + _, err = i.l.WriteTo(buf.Bytes(), dst) + if err != nil { + i.log.Errorf("failed to send layers: %v", err) + } +} + +// Passsthru to tun write. +func (i *Interface) Write(b []byte) (int, error) { + return i.tun.device.Write(b) +} + +// Listen to events from the tun device. +func (i *Interface) eventReader() { + i.log.Debug("event worker - started") + + // For each event received, process it. + for event := range i.tun.device.Events() { + // If event is an MTU change, update our MTU. + if event&tun.EventMTUUpdate != 0 { + // Get the device's current MTU. + mtu, err := i.tun.device.MTU() + if err != nil { + i.log.Errorf("failed to load updated MTU of device: %v", err) + continue + } + + // If the MTU is negative, that isn't valid. + if mtu < 0 { + i.log.Errorf("MTU not updated to negative value: %v", mtu) + continue + } + + // Determine if the MTU set is larger than our maximum based on listern's max size. + var tooLarge string + maxMTU := i.l.MaxMessageSize() - 50 + if mtu > maxMTU { + tooLarge = fmt.Sprintf(" (too large, capped at %v)", maxMTU) + mtu = maxMTU + } + + // Update the MTU, getting the prior MTU. + old := i.tun.mtu.Swap(int32(mtu)) + + // If the MTU changed, we should update the buffer size in the packet reader. + if int(old) != mtu { + go func() { + i.tun.mtuc <- mtu + }() + i.log.Debugf("MTU updated: %v%s", mtu, tooLarge) + } + } + + // If the interface is going up, we should update our state. + if event&tun.EventUp != 0 { + i.log.Debug("Interface up requested") + i.Up() + } + + // If the interface is going down, we should update our state. + if event&tun.EventDown != 0 { + i.log.Debug("Interface down requested") + i.Down() + } + } + + i.log.Debug("event worker - stopped") +} + +// Read packets from the tun device, and process them. +func (i *Interface) packetReader() { + // When stopping, we want to ensure this packet reader stopped before we release the interface. + i.state.stopping.Add(1) + defer func() { + i.log.Debug("TUN packet reader - stopped") + i.state.stopping.Done() + }() + + // Setup packet parsers. + var ip4 layers.IPv4 + ip4Parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4) + ip4Parser.IgnoreUnsupported = true + var ip6 layers.IPv6 + ip6Parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip6) + ip6Parser.IgnoreUnsupported = true + decoded := []gopacket.LayerType{} + + // Setup buffer with current MTU value. + i.log.Debug("TUN packet reader - started") + buf := make([]byte, i.MTU()) + + // Start reading from the tun device. + for { + n, err := i.tun.device.Read(buf) + + // If packet received data, parse it. + if n > 1 { + packet := buf[:n] + switch packet[0] >> 4 { + case 4: + // Parse IPv4 packet. + decoded = nil + err := ip4Parser.DecodeLayers(packet, &decoded) + if err == nil { + // Parse the IP address for the destination. + addr, ok := netip.AddrFromSlice(ip4.DstIP) + if !ok { + continue + } + + // Find the MAC address for the IP. + mac, err := i.GetMacFor(addr) + if err != nil { + i.log.Error(err) + continue + } + + // Get the UDP destination for the MAC address. + dst := i.GetDestinationFor(mac) + if dst == nil { + continue + } + + // Setup ethernet layer. + eth := layers.Ethernet{ + SrcMAC: i.tun.mac, + DstMAC: mac, + EthernetType: layers.EthernetTypeIPv4, + } + + // Setup upper layer masquerade. + masq := Masquerade{ + MData: ip4.Payload, + MLayerType: ip4.Protocol.LayerType(), + } + + // Send data to destination. + i.SendLayers(dst, ð, &ip4, &masq) + } + case 6: + // Pase IPv6 packet. + decoded = nil + err := ip6Parser.DecodeLayers(packet, &decoded) + if err == nil { + // Parse the IP address for the destination. + addr, ok := netip.AddrFromSlice(ip6.DstIP) + if !ok { + continue + } + + // Find the MAC address for the IP. + mac, err := i.GetMacFor(addr) + if err != nil { + i.log.Error(err) + continue + } + + // Get the UDP destination for the MAC address. + dst := i.GetDestinationFor(mac) + if dst == nil { + continue + } + + // Setup ethernet layer. + eth := layers.Ethernet{ + SrcMAC: i.tun.mac, + DstMAC: mac, + EthernetType: layers.EthernetTypeIPv6, + } + + // Setup upper layer masquerade. + masq := Masquerade{ + MData: ip6.Payload, + MLayerType: ip6.NextLayerType(), + } + + // Send data to destination. + i.SendLayers(dst, ð, &ip6, &masq) + } + } + } + + // If we received an error, check if we should stop. + if err != nil { + // If the error is relating to segments, just continue. + if errors.Is(err, tun.ErrTooManySegments) { + i.log.Debugf("dropped some packets from multi-segment read: %v", err) + continue + } + // If this device isn't closed, we should close it for other errors. + if !i.IsClosed() { + // Log error if its not an closed error. + if !errors.Is(err, os.ErrClosed) { + i.log.Errorf("failed to read packet from TUN device: %v", err) + } + // Close this interface. + go i.Close() + } + // Stop the packet reader here. + return + } + + // If the MTU has a change request, make a new buffer. + select { + case newMTU, ok := <-i.tun.mtuc: + if ok { + buf = make([]byte, newMTU) + } + default: + } + } +} diff --git a/interface_cmd.go b/interface_cmd.go new file mode 100644 index 0000000..bac6220 --- /dev/null +++ b/interface_cmd.go @@ -0,0 +1,595 @@ +package main + +import ( + "context" + "fmt" + "os" + "time" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" + "github.com/jedib0t/go-pretty/v6/table" +) + +// Command to list interfaces on an listener. +type InterfaceListCmd struct{} + +func (a *InterfaceListCmd) Run(l *ListenerCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Pull list of interface on the requested listener. + listener := &pb.ListenerRequestWithName{Name: l.name()} + r, err := c.ListInterfaces(ctx, listener) + if err != nil { + return + } + + // Setup table for interfaces. + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"Name", "VNI", "MTU", "Permanent"}) + + // Add row for each interface. + for _, ifce := range r.Interfaces { + t.AppendRow([]interface{}{ifce.Name, ifce.Vni, ifce.Mtu, ifce.Permanent}) + } + + // Render the table. + t.Render() + return +} + +// Command to add interface to an listener. +type InterfaceAddCmd struct { + VNI uint32 `help:"VXLAN VNI" required:""` + MTU int32 `help:"MTU of interface, 0 will result in calculated value" default:"0"` + Permanent bool `help:"Should listener be saved to disk?"` +} + +func (a *InterfaceAddCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Setup interface request. + ifce := &pb.AddInterfaceRequest{ + ListenerName: l.name(), + Name: i.name(), + Vni: a.VNI, + Mtu: a.MTU, + Permanent: a.Permanent, + } + + // Attempt to add the interface. + _, err = c.AddInterface(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Added interface to listener.") + return +} + +// Command to remove an interface on an listener. +type InterfaceRemoveCmd struct{} + +func (a *InterfaceRemoveCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Setup interface request. + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + + // Attempt to add the interface. + _, err = c.RemoveInterface(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Removed interface from listener.") + return +} + +// Command to set interface's MTU. +type InterfaceSetMTUCmd struct { + MTU int32 `help:"Interface MTU value (0 will result in calculated value)" default:"0"` +} + +func (a *InterfaceSetMTUCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to update interface's MTU. + ifce := &pb.InterfaceMTURequest{ + ListenerName: l.name(), + Name: i.name(), + Mtu: a.MTU, + } + _, err = c.SetInterfaceMTU(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Updated MTU.") + return +} + +// Command to set interface's MAC address. +type InterfaceSetMACAddressCmd struct { + MAC string `help:"The MAC Address to set"` +} + +func (a *InterfaceSetMACAddressCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to update interface's MAC address. + ifce := &pb.InterfaceMACAddressRequest{ + ListenerName: l.name(), + Name: i.name(), + Mac: a.MAC, + } + _, err = c.SetInterfaceMACAddress(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Updated MAC Address.") + return +} + +// Command to get interface's MAC address. +type InterfaceGetMACAddressCmd struct{} + +func (a *InterfaceGetMACAddressCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to get interface's MAC address. + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + r, err := c.GetInterfaceMACAddress(ctx, ifce) + if err != nil { + return + } + + // Setup table for MAC listing. + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"MAC Address"}) + + // Add row for the MAC address. + t.AppendRow([]interface{}{r.Mac}) + + // Render the table. + t.Render() + return +} + +// Command to set interface's IP addresses. +type InterfaceSetIPAddressesCmd struct { + IPAddress []string `help:"The IP address(es) to set"` +} + +func (a *InterfaceSetIPAddressesCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to update interface's IP address(es). + ifce := &pb.InterfaceIPAddressesRequest{ + ListenerName: l.name(), + Name: i.name(), + IpAddress: a.IPAddress, + } + _, err = c.SetInterfaceIPAddresses(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Updated IP Address(es).") + return +} + +// Command to get interface's MAC Address. +type InterfaceGetIPAddressesCmd struct{} + +func (a *InterfaceGetIPAddressesCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to get interface's IP address(es). + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + r, err := c.GetInterfaceIPAddresses(ctx, ifce) + if err != nil { + return + } + + // Setup table for IP list. + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"IP Address"}) + + // Add row for the IP address(es). + for _, ipAddress := range r.IpAddress { + t.AppendRow([]interface{}{ipAddress}) + } + + // Render the table. + t.Render() + return +} + +// Command to add an MAC entry to an interface. +type InterfaceAddMACEntryCmd struct { + MAC string `help:"MAC address to route (00:00:00:00:00:00 is treated as default route)" default:"00:00:00:00:00:00"` + Destination string `help:"The IP address to send vxlan traffic" required:""` + Permanent bool `help:"Should the MAC entry be saved to disk?"` +} + +func (a *InterfaceAddMACEntryCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to add MAC entry to an interface. + ifce := &pb.InterfaceMacEntryRequest{ + ListenerName: l.name(), + Name: i.name(), + Mac: a.MAC, + Destination: a.Destination, + Permanent: a.Permanent, + } + _, err = c.InterfaceAddMACEntry(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Added MAC entry.") + return +} + +// Command to remove an MAC entry from an interface. +type InterfaceRemoveMACEntryCmd struct { + MAC string `help:"MAC address" required:""` + Destination string `help:"The IP address" required:""` +} + +func (a *InterfaceRemoveMACEntryCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to remove an MAC entry from an interface. + ifce := &pb.InterfaceRemoveMacEntryRequest{ + ListenerName: l.name(), + Name: i.name(), + Mac: a.MAC, + Destination: a.Destination, + } + _, err = c.InterfaceRemoveMACEntry(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Removed MAC entry.") + return +} + +// Command to get MAC entries on an interface. +type InterfaceGetMACEntriesCmd struct{} + +func (a *InterfaceGetMACEntriesCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to get MAC entries on an interface. + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + r, err := c.InterfaceGetMACEntries(ctx, ifce) + if err != nil { + return + } + + // Setup table for MAC entries. + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"MAC Address", "Destination", "Permanent"}) + + // Add rows for entries. + for _, ent := range r.Entries { + t.AppendRow([]interface{}{ent.Mac, ent.Destination, ent.Permanent}) + } + + // Render the table. + t.Render() + return +} + +// Command to remove all mac entries from an interface. +type InterfaceFlushMACTableCmd struct{} + +func (a *InterfaceFlushMACTableCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to flush the MAC entry table on an interface. + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + _, err = c.InterfaceFlushMACTable(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Flushed MAC entry table.") + return +} + +// Command to add an MAC entry to an interface. +type InterfaceAddStaticARPEntryCmd struct { + Address string `help:"IP address" required:""` + MAC string `help:"MAC address" required:""` + Permanent bool `help:"Should the ARP entry be saved to disk?"` +} + +func (a *InterfaceAddStaticARPEntryCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to add an static ARP entry to an interface. + ifce := &pb.InterfaceARPEntryRequest{ + ListenerName: l.name(), + Name: i.name(), + Address: a.Address, + Mac: a.MAC, + Permanent: a.Permanent, + } + _, err = c.InterfaceAddStaticARPEntry(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Added static ARP entry.") + return +} + +// Command to remove an MAC entry from an interface. +type InterfaceRemoveARPEntryCmd struct { + Address string `help:"IP address" required:""` +} + +func (a *InterfaceRemoveARPEntryCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to remove an ARP entry from a table. + ifce := &pb.InterfaceRemoveARPEntryRequest{ + ListenerName: l.name(), + Name: i.name(), + Address: a.Address, + } + _, err = c.InterfaceRemoveARPEntry(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Removed ARP entry.") + return +} + +// Command to get MAC entries on an interface. +type InterfaceGetARPEntriesCmd struct{} + +func (a *InterfaceGetARPEntriesCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt tp update listener's max message size. + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + r, err := c.InterfaceGetARPEntries(ctx, ifce) + if err != nil { + return + } + + // Setup table for interfaces. + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"IP Address", "MAC Address", "Expires", "Permanent"}) + + // Add row for the IP address(es). + for _, ent := range r.Entries { + t.AppendRow([]interface{}{ent.Address, ent.Mac, ent.Expires, ent.Permanent}) + } + + // Render the table. + t.Render() + return +} + +// Command to remove all mac entries from an interface. +type InterfaceFlushARPTableCmd struct{} + +func (a *InterfaceFlushARPTableCmd) Run(l *ListenerCmd, i *InterfaceCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt tp update listener's max message size. + ifce := &pb.InterfaceRequestWithName{ + ListenerName: l.name(), + Name: i.name(), + } + _, err = c.InterfaceFlushARPTable(ctx, ifce) + if err != nil { + return + } + + fmt.Println("Flushed ARP entry table.") + return +} + +// Commands managing listeners. +type InterfaceCmd struct { + List InterfaceListCmd `cmd:""` + Name struct { + Name string `arg:"" help:"Interface name" required:""` + Add InterfaceAddCmd `cmd:""` + Remove InterfaceRemoveCmd `cmd:""` + SetMTU InterfaceSetMTUCmd `cmd:""` + SetMACAddress InterfaceSetMACAddressCmd `cmd:""` + GetMACAddress InterfaceGetMACAddressCmd `cmd:""` + SetIPAddresses InterfaceSetIPAddressesCmd `cmd:""` + GetIPAddresses InterfaceGetIPAddressesCmd `cmd:""` + AddMACEntry InterfaceAddMACEntryCmd `cmd:""` + RemoveMACEntry InterfaceRemoveMACEntryCmd `cmd:""` + GetMACEntries InterfaceGetMACEntriesCmd `cmd:""` + FlushMACTable InterfaceFlushMACTableCmd `cmd:""` + AddStaticARPEntry InterfaceAddStaticARPEntryCmd `cmd:""` + RemoveARPEntry InterfaceRemoveARPEntryCmd `cmd:""` + GetARPEntries InterfaceGetARPEntriesCmd `cmd:""` + FlushARPTable InterfaceFlushARPTableCmd `cmd:""` + } `arg:""` +} + +// Returns the interface name. +func (i *InterfaceCmd) name() string { + return i.Name.Name +} diff --git a/interface_grpc.go b/interface_grpc.go new file mode 100644 index 0000000..eeb91ae --- /dev/null +++ b/interface_grpc.go @@ -0,0 +1,395 @@ +package main + +import ( + "context" + "fmt" + "net" + "net/netip" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" +) + +// List interfaces on a listener. +func (s *GRPCServer) ListInterfaces(ctx context.Context, in *pb.ListenerRequestWithName) (*pb.ListInterfacesReply, error) { + reply := new(pb.ListInterfacesReply) + app.Net.Lock() + for _, listener := range app.Net.Listeners { + if listener.Name == in.Name { + listener.net.RLock() + for _, iface := range listener.net.interfaces { + ifce := &pb.Interface{ + Name: iface.Name(), + Vni: iface.VNI(), + Mtu: int32(iface.MTU()), + Permanent: iface.Permanent, + } + reply.Interfaces = append(reply.Interfaces, ifce) + } + listener.net.RUnlock() + break + } + } + app.Net.Unlock() + return reply, nil +} + +// Add interface to the listener. +func (s *GRPCServer) AddInterface(ctx context.Context, in *pb.AddInterfaceRequest) (*pb.Empty, error) { + // Find listener. + list, err := s.FindListener(in.ListenerName) + if err != nil { + return nil, err + } + + // Add interface to listener. + _, err = NewInterface(in.Name, in.Vni, int(in.Mtu), list, in.Permanent) + if err != nil { + return nil, err + } + + return new(pb.Empty), nil +} + +// Find interface on listener. +func (s *GRPCServer) FindInterface(listenerName, name string) (list *Listener, ifce *Interface, err error) { + // Find listener + list, err = s.FindListener(listenerName) + if err != nil { + return nil, nil, err + } + + // Find interface. + list.net.RLock() + for _, iface := range list.net.interfaces { + if iface.Name() == name { + ifce = iface + break + } + } + list.net.RUnlock() + + // If no interface found, error. + if ifce == nil { + return nil, nil, fmt.Errorf("no interface with name: %s", name) + } + return +} + +// Remove interface from the listener. +func (s *GRPCServer) RemoveInterface(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.Empty, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Close the interface. + err = ifce.Close() + if err != nil { + return nil, err + } + return new(pb.Empty), nil +} + +// Set interface's MTU. +func (s *GRPCServer) SetInterfaceMTU(ctx context.Context, in *pb.InterfaceMTURequest) (*pb.Empty, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Set the MTU for the interface. + ifce.SetMTU(int(in.Mtu)) + + return new(pb.Empty), nil +} + +// Get interface's MTU. +func (s *GRPCServer) GetInterfaceMTU(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.InterfaceMTUReply, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Get the MTU for the interface. + reply := &pb.InterfaceMTUReply{ + Mtu: int32(ifce.MTU()), + } + + return reply, nil +} + +// Set interface's MAC address. +func (s *GRPCServer) SetInterfaceMACAddress(ctx context.Context, in *pb.InterfaceMACAddressRequest) (*pb.Empty, error) { + // Parse MAC Address. + mac, err := net.ParseMAC(in.Mac) + if err != nil { + return nil, fmt.Errorf("unable to parse MAC address: %v", err) + } + + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Set the MAC Address for the interface. + err = ifce.SetMACAddress(mac) + if err != nil { + return nil, err + } + + return new(pb.Empty), nil +} + +// Get interface's MAC address. +func (s *GRPCServer) GetInterfaceMACAddress(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.InterfaceMACAddressReply, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Get the MAC address of the interface. + mac, err := ifce.GetMACAddress() + if err != nil { + return nil, err + } + + // Set reply with MAC address. + reply := &pb.InterfaceMACAddressReply{ + Mac: mac.String(), + } + + return reply, nil +} + +// Set interface's IP addresses. +func (s *GRPCServer) SetInterfaceIPAddresses(ctx context.Context, in *pb.InterfaceIPAddressesRequest) (*pb.Empty, error) { + // Parse provided IP addresses. + var prefixes []netip.Prefix + for _, ipAddress := range in.IpAddress { + prefix, err := netip.ParsePrefix(ipAddress) + if err != nil { + return nil, fmt.Errorf("failed to parse IP Address %s: %v", ipAddress, err) + } + prefixes = append(prefixes, prefix) + } + + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Set the IP Addresses for the interface. + err = ifce.SetIPAddresses(prefixes) + if err != nil { + return nil, err + } + + return new(pb.Empty), nil +} + +// Get interface's IP addresses. +func (s *GRPCServer) GetInterfaceIPAddresses(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.InterfaceIPAddressesReply, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Get the IP address(es) of the interface. + prefixes, err := ifce.GetIPAddresses() + if err != nil { + return nil, err + } + + // Set reply with IP address(es). + reply := new(pb.InterfaceIPAddressesReply) + for _, prefix := range prefixes { + reply.IpAddress = append(reply.IpAddress, prefix.String()) + } + + return reply, nil +} + +// Add MAC entry to an interface. +func (s *GRPCServer) InterfaceAddMACEntry(ctx context.Context, in *pb.InterfaceMacEntryRequest) (*pb.Empty, error) { + // Parse MAC Address. + mac, err := net.ParseMAC(in.Mac) + if err != nil { + return nil, fmt.Errorf("unable to parse MAC address: %v", err) + } + + // Parse destination + dst := net.ParseIP(in.Destination) + if dst == nil { + return nil, fmt.Errorf("unable to parse IP address") + } + + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Add the MAC entry. + err = ifce.AddMACEntry(mac, dst, in.Permanent) + if err != nil { + return nil, err + } + + return new(pb.Empty), nil +} + +// Remove MAC entry from an interface. +func (s *GRPCServer) InterfaceRemoveMACEntry(ctx context.Context, in *pb.InterfaceRemoveMacEntryRequest) (*pb.Empty, error) { + // Parse MAC Address. + mac, err := net.ParseMAC(in.Mac) + if err != nil { + return nil, fmt.Errorf("unable to parse MAC address: %v", err) + } + + // Parse destination + dst := net.ParseIP(in.Destination) + if dst == nil { + return nil, fmt.Errorf("unable to parse IP address") + } + + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Remove the MAC entry. + err = ifce.RemoveMACEntry(mac, dst) + if err != nil { + return nil, err + } + + return new(pb.Empty), nil +} + +// Get MAC entries on interface. +func (s *GRPCServer) InterfaceGetMACEntries(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.InterfaceMacEntryReply, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Get MAC entries and make reply. + entries := ifce.GetMACEntries() + reply := new(pb.InterfaceMacEntryReply) + for _, entry := range entries { + ent := &pb.MacEntry{ + Mac: entry.MAC.String(), + Destination: entry.Dst.IP.String(), + Permanent: entry.Permanent, + } + reply.Entries = append(reply.Entries, ent) + } + + return reply, nil +} + +// Flush MAC table on interface. +func (s *GRPCServer) InterfaceFlushMACTable(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.Empty, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Flush MAC table. + ifce.FlushMACTable() + return new(pb.Empty), nil +} + +// Add static ARP entry to an interface. +func (s *GRPCServer) InterfaceAddStaticARPEntry(ctx context.Context, in *pb.InterfaceARPEntryRequest) (*pb.Empty, error) { + // Parse IP address + addr, err := netip.ParseAddr(in.Address) + if err != nil { + return nil, fmt.Errorf("unable to parse IP address: %v", err) + } + + // Parse MAC Address. + mac, err := net.ParseMAC(in.Mac) + if err != nil { + return nil, fmt.Errorf("unable to parse MAC address: %v", err) + } + + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Add the ARP entry. + ifce.AddStaticARPEntry(addr, mac, in.Permanent) + return new(pb.Empty), nil +} + +// Remove ARP entry from an interface. +func (s *GRPCServer) InterfaceRemoveARPEntry(ctx context.Context, in *pb.InterfaceRemoveARPEntryRequest) (*pb.Empty, error) { + // Parse IP address + addr, err := netip.ParseAddr(in.Address) + if err != nil { + return nil, fmt.Errorf("unable to parse IP address: %v", err) + } + + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Remove the ARP entry. + err = ifce.RemoveARPEntry(addr) + if err != nil { + return nil, err + } + return new(pb.Empty), nil +} + +// Get ARP entries on interface. +func (s *GRPCServer) InterfaceGetARPEntries(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.InterfaceArpEntryReply, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Get MAC entries and make reply. + entries := ifce.GetARPEntries() + reply := new(pb.InterfaceArpEntryReply) + for _, entry := range entries { + ent := &pb.ArpEntry{ + Address: entry.Addr.String(), + Mac: entry.MAC.String(), + Expires: entry.Expires.String(), + Permanent: entry.Permanent, + } + reply.Entries = append(reply.Entries, ent) + } + + return reply, nil +} + +// Flush ARP table on interface. +func (s *GRPCServer) InterfaceFlushARPTable(ctx context.Context, in *pb.InterfaceRequestWithName) (*pb.Empty, error) { + // Find interface. + _, ifce, err := s.FindInterface(in.ListenerName, in.Name) + if err != nil { + return nil, err + } + + // Flush ARP table. + ifce.FlushARPTable() + return new(pb.Empty), nil +} diff --git a/listener.go b/listener.go new file mode 100644 index 0000000..c3c8a76 --- /dev/null +++ b/listener.go @@ -0,0 +1,257 @@ +package main + +import ( + "errors" + "fmt" + "net" + "sync" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + log "github.com/sirupsen/logrus" +) + +// The base of a vxlan connection is the port listener. +// The port listener listens for vxlan packets on a port, +// and it passes matching VNI values with interfaces. +// Interfaces are added to the listener, which processes +// vxlan packets. +type Listener struct { + net struct { + stopping sync.WaitGroup + addr *net.UDPAddr + is4 bool + maxMessageSize int + maxMessageSizeC chan int + conn *net.UDPConn + promisc *Promiscuous + interfaces []*Interface + sync.RWMutex + } + + Name string + Permanent bool + closed chan struct{} + log *log.Entry +} + +// Check if IP address is all zero. +func isZeroAddr(ip net.IP) bool { + for _, b := range ip { + if b != 0x0 { + return false + } + } + return true +} + +// Make a new listener on the specified address. This +// listener is added to the app listener list, and errors +// on existing listeners for the specified address. +func NewListener(name, address string, maxMessageSize int, perm bool) (l *Listener, err error) { + // Verify the specified address is valid. + addr, err := net.ResolveUDPAddr("udp", address) + if err != nil { + return + } + is4 := addr.IP.To4() != nil + + // Verify no listeners exist with this address. + app.Net.Lock() + defer app.Net.Unlock() + for _, listener := range app.Net.Listeners { + if listener.PrettyName() == addr.String() { + return nil, fmt.Errorf("listener already exists with address %s", addr.String()) + } + if listener.Name == name { + return nil, fmt.Errorf("listener already exists with name %s", name) + } + } + + // On Windows, there is no public way to configure hardware vxlan offloading. + // This in term filters packets that are destined to non-broadcast MAC addresses + // in vxlan packets. Which prevents us from receiving the packets, so we set the + // interface to promiscuous mode to allow us to receive packets. We can only do + // this if the IP address provided is an absolute address. + var promisc *Promiscuous + if !isZeroAddr(addr.IP) { + promisc, err = SetInterfacePromiscuous(addr.IP) + if err != nil { + return + } + } + + // Start listening on the specified address. + conn, err := net.ListenUDP("udp", addr) + if err != nil { + return + } + + // Save listener details. + l = new(Listener) + l.Name = name + l.net.addr = addr + l.net.is4 = is4 + l.net.maxMessageSize = maxMessageSize + l.net.maxMessageSizeC = make(chan int) + l.net.conn = conn + l.net.promisc = promisc + l.log = log.WithFields(log.Fields{ + "listener": addr.String(), + }) + l.closed = make(chan struct{}) + l.Permanent = perm + app.Net.Listeners = append(app.Net.Listeners, l) + + // Start reading packets on the listener. + go l.packetReader() + + // Inform that we started a listern. + l.log.Print("Listener started.") + return +} + +// Get the current maximum message size. +func (l *Listener) MaxMessageSize() int { + l.net.RLock() + defer l.net.RUnlock() + return l.net.maxMessageSize +} + +// Set the maximum message size to a new value. +func (l *Listener) SetMaxMessageSize(size int) { + if size <= 1 { + return + } + l.net.Lock() + l.net.maxMessageSize = size + l.net.Unlock() + go func() { + l.net.maxMessageSizeC <- size + }() +} + +// Close the listener, and its interfaces. +func (l *Listener) Close() (err error) { + // Ensure proper multi tasking. + l.net.Lock() + l.log.Debug("Listener is closing.") + + // Close the connection. + err = l.net.conn.Close() + if l.net.promisc != nil { + l.net.promisc.Close() + } + close(l.closed) + + // Wait for packet readers to stop. + l.net.stopping.Wait() + + // Remove self from app. + app.Net.Lock() + defer app.Net.Unlock() + for i, listener := range app.Net.Listeners { + if listener == l { + app.Net.Listeners = append(app.Net.Listeners[:i], app.Net.Listeners[i+1:]...) + break + } + } + + // The interfaces will be acquiring lock here while closing. + l.net.Unlock() + for len(l.net.interfaces) >= 1 { + l.net.interfaces[0].Close() + } + + l.log.Print("Listener closed.") + return +} + +// Get listener name. +func (l *Listener) PrettyName() string { + return l.net.addr.String() +} + +// Get listener address. +func (l *Listener) Addr() net.Addr { + return l.net.addr +} + +// Read packets and parse. +func (l *Listener) packetReader() { + // When stopping, we need to wait for this packet reader to finish. + l.net.stopping.Add(1) + defer func() { + l.log.Debug("packet reader - stopped") + l.net.stopping.Done() + }() + + // Setup packet decoder. + var vxlan layers.VXLAN + var eth layers.Ethernet + var ip4 layers.IPv4 + var ip6 layers.IPv6 + var arp layers.ARP + parser := gopacket.NewDecodingLayerParser(layers.LayerTypeVXLAN, &vxlan, ð, &ip4, &ip6, &arp) + parser.IgnoreUnsupported = true + var decoded []gopacket.LayerType + + // Start reading packets, with current buffer size. + l.log.Debug("packet reader - started") + buf := make([]byte, l.net.maxMessageSize) + for { + // Read packet. + n, err := l.net.conn.Read(buf) + if err != nil { + if errors.Is(err, net.ErrClosed) { + break + } + l.log.Errorf("received error reading from listener: %v", err) + } + + // Only process packets larger than a vxlan header. + if n > 38 { + // Attempt to parse vxlan and its layers, up to IP and ARP. + // Parsing any further is a waste of processing power. + decoded = nil + packet := buf[:n] + err = parser.DecodeLayers(packet, &decoded) + + // If we successfully parsed a packet, route it accordingly. + if err == nil { + // To ensure the interfaces do not change as we're handling the packet, lock it. + l.net.RLock() + for _, ifce := range l.net.interfaces { + // If this interface has the decoded VNI, pass the packet to it. + if ifce.vni == vxlan.VNI { + // Depending on what layer type was decoded, pass to the interface. + for _, layerType := range decoded { + if layerType == layers.LayerTypeARP { + ifce.HandleARP(arp) + } else if layerType == layers.LayerTypeIPv4 { + ifce.HandleIPv4(eth, ip4) + } else if layerType == layers.LayerTypeIPv6 { + ifce.HandleIPv6(eth, ip6) + } + } + } + } + l.net.RUnlock() + } + } + + // If the max message size has a change request, make a new buffer and save change. + select { + case newSize, ok := <-l.net.maxMessageSizeC: + if ok { + buf = make([]byte, newSize) + } + default: + } + } +} + +// Write data to destination. +func (l *Listener) WriteTo(b []byte, addr *net.UDPAddr) (int, error) { + return l.net.conn.WriteTo(b, addr) +} diff --git a/listener_cmd.go b/listener_cmd.go new file mode 100644 index 0000000..07163ff --- /dev/null +++ b/listener_cmd.go @@ -0,0 +1,164 @@ +package main + +import ( + "context" + "fmt" + "os" + "time" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" + "github.com/jedib0t/go-pretty/v6/table" +) + +// Command to list listeners +type ListenerListCmd struct{} + +func (a *ListenerListCmd) Run() (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Read listener list. + r, err := c.ListListeners(ctx, &pb.Empty{}) + if err != nil { + return + } + + // Verify there are listeners. + if len(r.Listeners) == 0 { + fmt.Println("No listeners running.") + return + } + + // Setup table for listener list. + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"Name", "Address", "Max Message Size", "Permanent"}) + + // Add rows for each listener. + for _, list := range r.Listeners { + t.AppendRow([]interface{}{list.Name, list.Address, list.MaxMessageSize, list.Permanent}) + } + + // Print the table. + t.Render() + return +} + +// Command to add an listener. +type ListenerAddCmd struct { + Address string `help:"Bind address (':4789' or '10.0.0.2:4789')" required:""` + MaxMessageSize int32 `help:"Max UDP message size" default:"1500"` + Permanent bool `help:"Should listener be saved to disk?"` +} + +func (a *ListenerAddCmd) Run(l *ListenerCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Setup listener config. + listener := &pb.Listener{ + Name: l.name(), + Address: a.Address, + MaxMessageSize: a.MaxMessageSize, + Permanent: a.Permanent, + } + + // Attempt to add an listener. + _, err = c.AddListener(ctx, listener) + if err != nil { + return + } + + fmt.Println("Added listener.") + return +} + +// Command to remove an listener. +type ListenerRemoveCmd struct{} + +func (a *ListenerRemoveCmd) Run(l *ListenerCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt to remove the listener. + listener := &pb.ListenerRequestWithName{Name: l.name()} + _, err = c.RemoveListener(ctx, listener) + if err != nil { + return + } + + fmt.Println("Removed listener.") + return +} + +// Command to set listener's max message size. +type ListenerSetMaxMessageSizeCmd struct { + Size int32 `help:"Max UDP message size" default:"1500"` +} + +func (a *ListenerSetMaxMessageSizeCmd) Run(l *ListenerCmd) (err error) { + // Connect to GRPC. + c, conn, err := NewGRPCClient() + if err != nil { + return + } + defer conn.Close() + + // Setup call timeout of 10 seconds. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Attempt tp update listener's max message size. + listener := &pb.ListenerMaxMessageSizeRequest{ + Name: l.name(), + Size: a.Size, + } + _, err = c.SetListenerMaxMessageSize(ctx, listener) + if err != nil { + return + } + + fmt.Println("Updated max message size.") + return +} + +// Commands managing listeners. +type ListenerCmd struct { + List ListenerListCmd `cmd:""` + Name struct { + Name string `arg:"" help:"Listener name" required:""` + Add ListenerAddCmd `cmd:""` + Remove ListenerRemoveCmd `cmd:""` + SetMaxMessageSize ListenerSetMaxMessageSizeCmd `cmd:""` + Interface InterfaceCmd `cmd:""` + } `arg:""` +} + +// Returns the listener name. +func (l *ListenerCmd) name() string { + return l.Name.Name +} diff --git a/listener_grpc.go b/listener_grpc.go new file mode 100644 index 0000000..d51cfad --- /dev/null +++ b/listener_grpc.go @@ -0,0 +1,97 @@ +package main + +import ( + "context" + "fmt" + + pb "github.com/grmrgecko/virtual-vxlan/vxlan" +) + +// Provide list of listeners. +func (s *GRPCServer) ListListeners(ctx context.Context, in *pb.Empty) (*pb.ListListenersReply, error) { + reply := new(pb.ListListenersReply) + app.Net.Lock() + for _, listener := range app.Net.Listeners { + list := &pb.Listener{ + Name: listener.Name, + Address: listener.PrettyName(), + MaxMessageSize: int32(listener.net.maxMessageSize), + Permanent: listener.Permanent, + } + reply.Listeners = append(reply.Listeners, list) + } + app.Net.Unlock() + return reply, nil +} + +// Add listener. +func (s *GRPCServer) AddListener(ctx context.Context, in *pb.Listener) (*pb.Empty, error) { + _, err := NewListener(in.Name, in.Address, int(in.MaxMessageSize), in.Permanent) + if err != nil { + return nil, err + } + return new(pb.Empty), nil +} + +// Find listener. +func (s *GRPCServer) FindListener(name string) (list *Listener, err error) { + // Find existing listener. + app.Net.Lock() + for _, listener := range app.Net.Listeners { + if listener.Name == name { + list = listener + break + } + } + app.Net.Unlock() + + // If no listener found, error. + if list == nil { + return nil, fmt.Errorf("no listener with name: %s", name) + } + return +} + +// Remove listener. +func (s *GRPCServer) RemoveListener(ctx context.Context, in *pb.ListenerRequestWithName) (*pb.Empty, error) { + list, err := s.FindListener(in.Name) + if err != nil { + return nil, err + } + + // Try and close the listener. + err = list.Close() + if err != nil { + return nil, err + } + + return new(pb.Empty), nil +} + +// Set listener's max message size. +func (s *GRPCServer) SetListenerMaxMessageSize(ctx context.Context, in *pb.ListenerMaxMessageSizeRequest) (*pb.Empty, error) { + list, err := s.FindListener(in.Name) + if err != nil { + return nil, err + } + + // Set the max message size for the listener. + list.SetMaxMessageSize(int(in.Size)) + + return new(pb.Empty), nil +} + +// Get listener's max message size. +func (s *GRPCServer) GetListenerMaxMessageSize(ctx context.Context, in *pb.ListenerRequestWithName) (*pb.ListenerMaxMessageSizeReply, error) { + list, err := s.FindListener(in.Name) + if err != nil { + return nil, err + } + + // Get the max message size for the listener. + reply := &pb.ListenerMaxMessageSizeReply{ + Size: int32(list.MaxMessageSize()), + } + + return reply, nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..00ce066 --- /dev/null +++ b/main.go @@ -0,0 +1,24 @@ +package main + +// Basic application info. +const ( + serviceName = "virtual-vxlan" + serviceDisplayName = "Virtual VXLAN" + serviceVendor = "com.mrgeckosmedia" + serviceDescription = "Virtual VXLAN using TUN interfaces" + serviceVersion = "0.1" + defaultConfigFile = "config.yaml" +) + +// The application start. +func main() { + // Parse the flags. + ctx := ParseFlags() + + // Configure logging. + flags.Log.Apply() + + // Run the command and exit. + err := ctx.Run() + ctx.FatalIfErrorf(err) +} diff --git a/masquerade.go b/masquerade.go new file mode 100644 index 0000000..f24ff2e --- /dev/null +++ b/masquerade.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + + "github.com/google/gopacket" +) + +// A packet layer that just contains pre-computed bytes. +type Masquerade struct { + MData []byte + MLayerType gopacket.LayerType +} + +// Return the layer type of this layer that we're masquerading. +func (m *Masquerade) LayerType() gopacket.LayerType { + return m.MLayerType +} + +// Encode data to buffer. +func (m *Masquerade) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { + // Get the data length, and ensure there is data. + length := len(m.MData) + if length < 1 { + return fmt.Errorf("invalid data") + } + + // Allocate bytes in buffer for this data. + bytes, err := b.PrependBytes(length) + if err != nil { + return err + } + + // copy data into bytes. + copy(bytes, m.MData) + return nil +} diff --git a/promiscuous_unix.go b/promiscuous_unix.go new file mode 100644 index 0000000..c5276f4 --- /dev/null +++ b/promiscuous_unix.go @@ -0,0 +1,23 @@ +//go:build !windows + +package main + +import "net" + +// I am focusing on Windows development currently, may fill this out if I find Linux needing promiscuous mode as well. +// Also may work on darwin support/bsd, depending on how I feel. +// For now, this is just a stub to make code happy. + +// Structure to store connections. +type Promiscuous struct { +} + +// Set interface to promiscuous mode, using the interface IP to identify the interface. +func SetInterfacePromiscuous(ifaceIP net.IP) (promisc *Promiscuous, err error) { + return new(Promiscuous), nil +} + +// Close promiscuous mode connection. +func (p *Promiscuous) Close() error { + return nil +} diff --git a/promiscuous_windows.go b/promiscuous_windows.go new file mode 100644 index 0000000..6bf7344 --- /dev/null +++ b/promiscuous_windows.go @@ -0,0 +1,164 @@ +package main + +import ( + "context" + "errors" + "fmt" + "net" + "strings" + "syscall" + "unsafe" + + "github.com/google/gopacket/pcap" + log "github.com/sirupsen/logrus" +) + +// Constants for setting promiscuous mode on windows. See Microsoft's documentation below: +// https://learn.microsoft.com/en-us/windows/win32/winsock/sio-rcvall +const ( + SIO_RCVALL = syscall.IOC_IN | syscall.IOC_VENDOR | 1 + + RCVALL_OFF = 0 + RCVALL_ON = 1 + RCVALL_SOCKETLEVELONLY = 2 + RCVALL_IPLEVEL = 3 +) + +// Structure to store connections. +type Promiscuous struct { + conn net.PacketConn + pcap *pcap.Handle +} + +// Set interface to promiscuous mode, using the interface IP to identify the interface. +func SetInterfacePromiscuous(ifaceIP net.IP) (promisc *Promiscuous, err error) { + promisc = new(Promiscuous) + // I have found npcap to be most performant, however due to its commercial nature, + // I do not include it in this project. You may install it to get its benefit, + // or the alternative will come into play. I am open to hearing of better solutions + // to this packet filtering problem, and better ideas. + err = promisc.tryPCap(ifaceIP) + if err != nil { + // If it fails because wpcap.dll is missing, try alternative method. + if strings.Contains(err.Error(), "wpcap.dll") { + log.Debug("Missing npcap, putting interface into promiscuous mode using SIO_RCVALL.") + // Put interface in promiscuous using ICMP listener. + err = promisc.tryICMPListen(ifaceIP) + if err != nil { + promisc = nil + return + } + // If regular error, just return the error. + } else { + promisc = nil + return + } + } + return +} + +// Use npcap to put interface in promiscuous mode. +func (p *Promiscuous) tryPCap(ifaceIP net.IP) (err error) { + // Find the windows interface name for the adapter with the IP we're binding. + ifs, err := pcap.FindAllDevs() + if err != nil { + return + } + foundIface := false + var piface pcap.Interface + for _, piface = range ifs { + // Find matching IP address. + for _, paddr := range piface.Addresses { + // If we found it, stop to keep interface reference. + if paddr.IP.Equal(ifaceIP) { + foundIface = true + break + } + } + // Stop here to prevent reference from being replaced. + if foundIface { + break + } + } + // If we didn't find the interface being bound to, stop here. + if !foundIface { + return fmt.Errorf("unable to find the interface with vxlan") + } + + // Open the pcap connection to put the interface in promiscuous mode. + log.Debugf("Putting adapter in promiscuous mode: %s (%s)", piface.Description, piface.Name) + p.pcap, err = pcap.OpenLive(piface.Name, 1, true, pcap.BlockForever) + if err != nil { + return + } + // To prevent the pcap from receiving packets, filter to just localhost + // traffic which should result in zero packets to capture. + err = p.pcap.SetBPFFilter("host 127.0.0.1") + if err != nil { + return + } + return +} + +// Use SIO_RCVALL to put interface in promiscuous mode. +func (p *Promiscuous) tryICMPListen(ifaceIP net.IP) (err error) { + // We need the syscall handle to put the interface in promiscuous mode with WSAIoctl. + var socketHandle syscall.Handle + + // Use listen config to get the syscall handle via the control function. + cfg := net.ListenConfig{ + Control: func(network, address string, c syscall.RawConn) error { + return c.Control(func(s uintptr) { + socketHandle = syscall.Handle(s) + }) + }, + } + + // Depending on IP address network, setup ICMP network. + network := "ip4:icmp" + if ifaceIP.To4() == nil { + network = "ip6:ipv6-icmp" + } + + // Use listen packet to start a connection. + p.conn, err = cfg.ListenPacket(context.Background(), network, ifaceIP.String()) + if err != nil { + return + } + + // Put interface in promiscuous mode. + cbbr := uint32(0) + flag := uint32(RCVALL_ON) + size := uint32(unsafe.Sizeof(flag)) + err = syscall.WSAIoctl(socketHandle, SIO_RCVALL, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &cbbr, nil, 0) + if err != nil { + p.conn.Close() + return + } + go p.connReader() + return +} + +// Read and discard packets read. +func (p *Promiscuous) connReader() { + buf := make([]byte, 500) + for { + _, _, err := p.conn.ReadFrom(buf) + if err != nil { + if errors.Is(err, net.ErrClosed) { + break + } + log.Errorf("received error reading in promiscuous: %v", err) + } + } +} + +// Close promiscuous mode connection. +func (p *Promiscuous) Close() (err error) { + if p.pcap != nil { + p.pcap.Close() + } else { + err = p.conn.Close() + } + return +} diff --git a/server_cmd.go b/server_cmd.go new file mode 100644 index 0000000..70b0419 --- /dev/null +++ b/server_cmd.go @@ -0,0 +1,130 @@ +package main + +import ( + "net" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/coreos/go-systemd/daemon" + "github.com/kardianos/service" + log "github.com/sirupsen/logrus" +) + +// Flags for the server command. +type ServerCmd struct { +} + +// The main App structure. +type App struct { + Net struct { + Listeners []*Listener + sync.Mutex + } + ControllerMac net.HardwareAddr + grpcServer *GRPCServer + Stop chan struct{} + UpdateConfig *UpdateConfig +} + +var app *App + +// Run the server. +func (a *ServerCmd) Run() error { + // Start a new app structure. + app = new(App) + app.ControllerMac = net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + app.Stop = make(chan struct{}) + { + // Read the configuration from file. + config := ReadConfig() + app.UpdateConfig = config.Update + + // Start the GRPC server for cli communication. + _, err := NewGRPCServer(config.RPCPath) + if err != nil { + return err + } + + // Apply the configuration read. + err = ApplyConfig(config) + // If error applying the config, we should fail. + if err != nil { + return err + } + } + + // Send notification that the service is ready. + daemon.SdNotify(false, daemon.SdNotifyReady) + + // Setup service. + if !service.Interactive() { + s := new(ServiceCmd) + svc, err := s.service() + if err != nil { + return err + } + go svc.Run() + } + + // Run the update loop to check for updates. + go app.RunUpdateLoop() + + // Inform that the service has started. + log.Println("Service started.") + + // Monitor common signals. + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) + + // Run program signal handler. + for { + // If this service is stopped by signal, this flag will be changed. + done := false + + // Check for a signal. + select { + case sig := <-c: + switch sig { + // If hangup signal receivied, reload the configurations. + case syscall.SIGHUP: + log.Println("Reloading configurations.") + + // Read the config. + config := ReadConfig() + app.UpdateConfig = config.Update + + // Apply any changes. + err := ApplyConfig(config) + if err != nil { + log.Println(err) + } + + // The default signal is either termination or interruption, + // so we should stop the loop. + default: + done = true + } + // If the app stops itself, mark as done. + case <-app.Stop: + done = true + } + if done { + close(app.Stop) + break + } + } + + // We're quitting, close out all listeners. + for len(app.Net.Listeners) >= 1 { + app.Net.Listeners[0].Close() + } + + // Stop the grpc server. + if app.grpcServer != nil { + app.grpcServer.Close() + } + + return nil +} diff --git a/service_cmd.go b/service_cmd.go new file mode 100644 index 0000000..0fd953f --- /dev/null +++ b/service_cmd.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + + "github.com/kardianos/service" +) + +var ServiceAction = []string{"start", "stop", "status", "restart", "install", "uninstall"} + +// Command to manage this service. +type ServiceCmd struct { + Action struct { + Action string `arg:"" enum:"${serviceActions}" help:"${serviceActions}" required:""` + } `arg:""` +} + +func (s *ServiceCmd) action() string { + return s.Action.Action +} + +func (s *ServiceCmd) Run() (err error) { + svc, err := s.service() + if err != nil { + return err + } + switch s.action() { + case ServiceAction[0]: + err = svc.Start() + case ServiceAction[1]: + err = svc.Stop() + case ServiceAction[2]: + status, err := svc.Status() + if err == nil { + switch status { + case service.StatusRunning: + fmt.Println("Service is running.") + case service.StatusStopped: + fmt.Println("Service is stopped.") + default: + fmt.Println("Service is in an unknown state.") + } + } + case ServiceAction[3]: + err = svc.Restart() + case ServiceAction[4]: + err = svc.Install() + case ServiceAction[5]: + err = svc.Uninstall() + } + if err != nil { + return err + } + if s.action() != ServiceAction[2] { + fmt.Println("Command executed successfully.") + } + return +} + +func (s *ServiceCmd) service() (service.Service, error) { + svcConfig := &service.Config{ + Name: serviceName, + DisplayName: serviceDisplayName, + Description: serviceDescription, + Arguments: []string{"server"}, + } + return service.New(s, svcConfig) +} + +func (s *ServiceCmd) Start(svc service.Service) error { + return nil +} + +func (s *ServiceCmd) Stop(svc service.Service) error { + app.Stop <- struct{}{} + return nil +} diff --git a/sysroot/Dockerfile b/sysroot/Dockerfile new file mode 100644 index 0000000..4195ad9 --- /dev/null +++ b/sysroot/Dockerfile @@ -0,0 +1,21 @@ +FROM debian:bookworm + +ARG PCAP_VER=1.10.5 + +RUN apt-get update; \ + apt-get --no-install-recommends -y -q install \ + build-essential \ + flex \ + bison \ + ca-certificates \ + wget + +RUN wget http://www.tcpdump.org/release/libpcap-${PCAP_VER}.tar.gz && \ + tar xvf libpcap-${PCAP_VER}.tar.gz && \ + cd libpcap-${PCAP_VER} && \ + ./configure --with-pcap=linux --prefix=/usr/ \ + --disable-usb --disable-bluetooth --disable-dbus --disable-rdma --disable-shared && \ + make && \ + make install && \ + cd ../ && \ + rm -Rf libpcap-${PCAP_VER} diff --git a/sysroot/README.md b/sysroot/README.md new file mode 100644 index 0000000..a68bdb8 --- /dev/null +++ b/sysroot/README.md @@ -0,0 +1,18 @@ +# Building sysroot + +- Install qemu binfmt support, and install docker with the buildx extension. + +- Ensure qemu is registered with: +```bash +ls /proc/sys/fs/binfmt_misc/ +``` + +- Setup buildx: +```bash +docker buildx create --name mybuilder --use +``` + +- Build the sysroot: +```bash +./build.sh +``` diff --git a/sysroot/build.sh b/sysroot/build.sh new file mode 100755 index 0000000..0941f8f --- /dev/null +++ b/sysroot/build.sh @@ -0,0 +1,131 @@ +#!/bin/env bash + +# Ensure we're in this directory when running the script. +cd -P -- "$(dirname -- "$0")" || exit 1 + +# Remove existing builds. +rm -Rf ./linux_*/ + +# Build the different sysroots in docker. +if ! docker buildx build --platform=linux/amd64,linux/arm64 --tag 'cross-sysroot:latest' --output 'type=local,dest=.' .; then + echo "Failed to build sysroot, please review sysroot/README.md for instructions on configuring environment." + exit 1 +fi + +# Setup exclude and include list for minimal sysroot building. +exclude_list=() +include_list=() + +exclude_list+=(--exclude "/bin") +exclude_list+=(--exclude "/boot") +exclude_list+=(--exclude "/boot*") +exclude_list+=(--exclude "/dev") +exclude_list+=(--exclude "/etc") +exclude_list+=(--exclude "/home") +exclude_list+=(--exclude "/lib/dhcpd") +exclude_list+=(--exclude "/lib/firmware") +exclude_list+=(--exclude "/lib/hdparm") +exclude_list+=(--exclude "/lib/ifupdown") +exclude_list+=(--exclude "/lib/modules") +exclude_list+=(--exclude "/lib/modprobe.d") +exclude_list+=(--exclude "/lib/modules-load.d") +exclude_list+=(--exclude "/lib/resolvconf") +exclude_list+=(--exclude "/lib/startpar") +exclude_list+=(--exclude "/lib/systemd") +exclude_list+=(--exclude "/lib/terminfo") +exclude_list+=(--exclude "/lib/udev") +exclude_list+=(--exclude "/lib/xtables") +exclude_list+=(--exclude "/lib/ssl/private") +exclude_list+=(--exclude "/lost+found") +exclude_list+=(--exclude "/media") +exclude_list+=(--exclude "/mnt") +exclude_list+=(--exclude "/proc") +exclude_list+=(--exclude "/root") +exclude_list+=(--exclude "/run") +exclude_list+=(--exclude "/sbin") +exclude_list+=(--exclude "/srv") +exclude_list+=(--exclude "/sys") +exclude_list+=(--exclude "/tmp") +exclude_list+=(--exclude "/usr/bin") +exclude_list+=(--exclude "/usr/games") +exclude_list+=(--exclude "/usr/sbin") +exclude_list+=(--exclude "/usr/share") +exclude_list+=(--exclude "/usr/src") +exclude_list+=(--exclude "/usr/local/bin") +exclude_list+=(--exclude "/usr/local/etc") +exclude_list+=(--exclude "/usr/local/games") +exclude_list+=(--exclude "/usr/local/man") +exclude_list+=(--exclude "/usr/local/sbin") +exclude_list+=(--exclude "/usr/local/share") +exclude_list+=(--exclude "/usr/local/src") +exclude_list+=(--exclude "/usr/lib/ssl/private") +exclude_list+=(--exclude "/var") +exclude_list+=(--exclude "/snap") +exclude_list+=(--exclude "*python*") + +include_list+=(--include "*.a") +include_list+=(--include "*.so") +include_list+=(--include "*.so.*") +include_list+=(--include "*.h") +include_list+=(--include "*.hh") +include_list+=(--include "*.hpp") +include_list+=(--include "*.hxx") +include_list+=(--include "*.pc") +include_list+=(--include "/lib") +include_list+=(--include "/lib32") +include_list+=(--include "/lib64") +include_list+=(--include "/libx32") +include_list+=(--include "*/") + +args=() +args+=(-a) +args+=(-z) +args+=(-m) +args+=(-d) +args+=(-h) +args+=(--keep-dirlinks) +args+=("--info=progress2") +args+=(--delete) +args+=(--prune-empty-dirs) +args+=(--sparse) +args+=(--links) +args+=(--copy-unsafe-links) +args+=("${exclude_list[@]}") +args+=("${include_list[@]}") +args+=(--exclude "*") + +# Make the sysroot environments minimal. +echo "Making sysroot the bare minimal." +for arch in linux_*; do + # Skip already minimal dirs. + if [[ $arch =~ _minimal ]]; then + continue + fi + + # Fix symbolic links. + ( + cd "$arch" || exit 1 + for slink in ./usr/lib64/ld-linux-x86-64.so.2 ./usr/lib/*-linux-gnu/*.so; do + if [[ -L $slink ]]; then + spath=$(readlink "$slink") + if grep -q "/${arch}/" <<<"$spath"; then + npath=$(sed "s/\/$arch\//.\//" <<<"$spath") + rpath=$(realpath -m --relative-to="$(dirname "$slink")" "$npath") + unlink "$slink" + ln -s "$rpath" "$slink" + fi + fi + done + ) + + # Run rsync to minimal. + rsync "${args[@]}" "$arch/" "${arch}_minimal/" + rm -Rf "${arch:?}/" + mv "${arch}_minimal/" "$arch/" +done + +# Clear build cache. +echo "Clearing build cache." +docker buildx prune --all --force + +echo "Done building sysroot." diff --git a/tun/errors.go b/tun/errors.go new file mode 100644 index 0000000..d56b760 --- /dev/null +++ b/tun/errors.go @@ -0,0 +1,12 @@ +package tun + +import ( + "errors" +) + +var ( + // ErrTooManySegments is returned by Device.Read() when segmentation + // overflows the length of supplied buffers. This error should not cause + // reads to cease. + ErrTooManySegments = errors.New("too many segments") +) diff --git a/tun/tun.go b/tun/tun.go new file mode 100644 index 0000000..ed3614a --- /dev/null +++ b/tun/tun.go @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. + */ + +package tun + +import ( + "net/netip" + "os" +) + +type Event int + +const ( + EventUp = 1 << iota + EventDown + EventMTUUpdate +) + +type Device interface { + // File returns the file descriptor of the device. + File() *os.File + + // Read one packet from the Device (without any additional headers). + // On a successful read it returns the number of bytes read. + Read(b []byte) (int, error) + + // Write one packet to the device (without any additional headers). + // On a successful write it returns the number of bytes written. + Write(b []byte) (int, error) + + // Set MTU on the device. + SetMTU(int) error + + // MTU returns the MTU of the Device. + MTU() (int, error) + + // Name returns the current name of the Device. + Name() (string, error) + + // Set IP Addresses for the device. + SetIPAddresses(addresses []netip.Prefix) error + + // Get IP Addresses for the device. + GetIPAddresses() ([]netip.Prefix, error) + + // Events returns a channel of type Event, which is fed Device events. + Events() <-chan Event + + // Close stops the Device and closes the Event channel. + Close() error +} diff --git a/tun/tun_linux.go b/tun/tun_linux.go new file mode 100644 index 0000000..f65b12e --- /dev/null +++ b/tun/tun_linux.go @@ -0,0 +1,562 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. + */ + +package tun + +/* Implementation of the TUN device interface for linux + */ + +import ( + "errors" + "fmt" + "net/netip" + "os" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + "golang.zx2c4.com/wireguard/rwcancel" +) + +const ( + cloneDevicePath = "/dev/net/tun" + ifReqSize = unix.IFNAMSIZ + 64 +) + +type NativeTun struct { + tunFile *os.File + index int32 // if index + errors chan error // async error handling + events chan Event // device related events + netlinkSock int + netlinkCancel *rwcancel.RWCancel + hackListenerClosed sync.Mutex + statusListenersShutdown chan struct{} + + closeOnce sync.Once + + nameOnce sync.Once // guards calling initNameCache, which sets following fields + nameCache string // name of interface + nameErr error +} + +func (tun *NativeTun) File() *os.File { + return tun.tunFile +} + +func (tun *NativeTun) routineHackListener() { + defer tun.hackListenerClosed.Unlock() + /* This is needed for the detection to work across network namespaces + * If you are reading this and know a better method, please get in touch. + */ + last := 0 + const ( + up = 1 + down = 2 + ) + for { + sysconn, err := tun.tunFile.SyscallConn() + if err != nil { + return + } + err2 := sysconn.Control(func(fd uintptr) { + _, err = unix.Write(int(fd), nil) + }) + if err2 != nil { + return + } + switch err { + case unix.EINVAL: + if last != up { + // If the tunnel is up, it reports that write() is + // allowed but we provided invalid data. + tun.events <- EventUp + last = up + } + case unix.EIO: + if last != down { + // If the tunnel is down, it reports that no I/O + // is possible, without checking our provided data. + tun.events <- EventDown + last = down + } + default: + return + } + select { + case <-time.After(time.Second): + // nothing + case <-tun.statusListenersShutdown: + return + } + } +} + +func createNetlinkSocket() (int, error) { + sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, unix.NETLINK_ROUTE) + if err != nil { + return -1, err + } + saddr := &unix.SockaddrNetlink{ + Family: unix.AF_NETLINK, + Groups: unix.RTMGRP_LINK | unix.RTMGRP_IPV4_IFADDR | unix.RTMGRP_IPV6_IFADDR, + } + err = unix.Bind(sock, saddr) + if err != nil { + return -1, err + } + return sock, nil +} + +func (tun *NativeTun) routineNetlinkListener() { + defer func() { + unix.Close(tun.netlinkSock) + tun.hackListenerClosed.Lock() + close(tun.events) + tun.netlinkCancel.Close() + }() + + for msg := make([]byte, 1<<16); ; { + var err error + var msgn int + for { + msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0) + if err == nil || !rwcancel.RetryAfterError(err) { + break + } + if !tun.netlinkCancel.ReadyRead() { + tun.errors <- fmt.Errorf("netlink socket closed: %w", err) + return + } + } + if err != nil { + tun.errors <- fmt.Errorf("failed to receive netlink message: %w", err) + return + } + + select { + case <-tun.statusListenersShutdown: + return + default: + } + + wasEverUp := false + for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; { + + hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0])) + + if int(hdr.Len) > len(remain) { + break + } + + switch hdr.Type { + case unix.NLMSG_DONE: + remain = []byte{} + + case unix.RTM_NEWLINK: + info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr])) + remain = remain[hdr.Len:] + + if info.Index != tun.index { + // not our interface + continue + } + + if info.Flags&unix.IFF_RUNNING != 0 { + tun.events <- EventUp + wasEverUp = true + } + + if info.Flags&unix.IFF_RUNNING == 0 { + // Don't emit EventDown before we've ever emitted EventUp. + // This avoids a startup race with HackListener, which + // might detect Up before we have finished reporting Down. + if wasEverUp { + tun.events <- EventDown + } + } + + tun.events <- EventMTUUpdate + + default: + remain = remain[hdr.Len:] + } + } + } +} + +func getIFIndex(name string) (int32, error) { + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, + 0, + ) + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + var ifr [ifReqSize]byte + copy(ifr[:], name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFINDEX), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return 0, errno + } + + return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil +} + +func (tun *NativeTun) setMTU(n int) error { + name, err := tun.Name() + if err != nil { + return err + } + + // open datagram socket + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, + 0, + ) + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + var ifr [ifReqSize]byte + copy(ifr[:], name) + *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return fmt.Errorf("failed to set MTU of TUN device: %w", errno) + } + + return nil +} + +func (tun *NativeTun) SetMTU(mtu int) error { + return tun.setMTU(mtu) +} + +func (tun *NativeTun) SetIPAddresses(addresses []netip.Prefix) error { + link, err := netlink.LinkByIndex(int(tun.index)) + if err != nil { + return err + } + var setAddr []*netlink.Addr + for _, address := range addresses { + addr, err := netlink.ParseAddr(address.String()) + if err != nil { + return err + } + setAddr = append(setAddr, addr) + } + addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL) + for _, address := range addrList { + found := -1 + for i, addr := range setAddr { + if addr.Equal(address) { + found = i + } + } + if found == -1 { + err = netlink.AddrDel(link, &address) + if err != nil { + return err + } + } else { + setAddr = append(setAddr[:found], setAddr[found+1:]...) + } + } + for _, addr := range setAddr { + err = netlink.AddrAdd(link, addr) + if err != nil { + return err + } + } + return nil +} + +func (tun *NativeTun) GetIPAddresses() ([]netip.Prefix, error) { + link, err := netlink.LinkByIndex(int(tun.index)) + if err != nil { + return nil, err + } + var prefixes []netip.Prefix + addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL) + for _, address := range addrList { + prefix, err := netip.ParsePrefix(address.String()) + if err != nil { + return nil, err + } + prefixes = append(prefixes, prefix) + } + return prefixes, nil +} + +func (tun *NativeTun) MTU() (int, error) { + name, err := tun.Name() + if err != nil { + return 0, err + } + + // open datagram socket + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM|unix.SOCK_CLOEXEC, + 0, + ) + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [ifReqSize]byte + copy(ifr[:], name) + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return 0, fmt.Errorf("failed to get MTU of TUN device: %w", errno) + } + + return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil +} + +func (tun *NativeTun) Name() (string, error) { + tun.nameOnce.Do(tun.initNameCache) + return tun.nameCache, tun.nameErr +} + +func (tun *NativeTun) initNameCache() { + tun.nameCache, tun.nameErr = tun.nameSlow() +} + +func (tun *NativeTun) nameSlow() (string, error) { + sysconn, err := tun.tunFile.SyscallConn() + if err != nil { + return "", err + } + var ifr [ifReqSize]byte + var errno syscall.Errno + err = sysconn.Control(func(fd uintptr) { + _, _, errno = unix.Syscall( + unix.SYS_IOCTL, + fd, + uintptr(unix.TUNGETIFF), + uintptr(unsafe.Pointer(&ifr[0])), + ) + }) + if err != nil { + return "", fmt.Errorf("failed to get name of TUN device: %w", err) + } + if errno != 0 { + return "", fmt.Errorf("failed to get name of TUN device: %w", errno) + } + return unix.ByteSliceToString(ifr[:]), nil +} + +func (tun *NativeTun) Write(b []byte) (int, error) { + n, err := tun.tunFile.Write(b) + if errors.Is(err, syscall.EBADFD) { + return 0, os.ErrClosed + } + if err != nil { + return 0, err + } + return n, nil +} + +func (tun *NativeTun) Read(b []byte) (int, error) { + select { + case err := <-tun.errors: + return 0, err + default: + n, err := tun.tunFile.Read(b) + if errors.Is(err, syscall.EBADFD) { + err = os.ErrClosed + } + if err != nil { + return 0, err + } + return n, nil + } +} + +func (tun *NativeTun) Events() <-chan Event { + return tun.events +} + +func (tun *NativeTun) Close() error { + var err1, err2 error + tun.closeOnce.Do(func() { + if tun.statusListenersShutdown != nil { + close(tun.statusListenersShutdown) + if tun.netlinkCancel != nil { + err1 = tun.netlinkCancel.Cancel() + } + } else if tun.events != nil { + close(tun.events) + } + err2 = tun.tunFile.Close() + }) + if err1 != nil { + return err1 + } + return err2 +} + +func (tun *NativeTun) initFromFlags(name string) error { + sc, err := tun.tunFile.SyscallConn() + if err != nil { + return err + } + if e := sc.Control(func(fd uintptr) { + var ( + ifr *unix.Ifreq + ) + ifr, err = unix.NewIfreq(name) + if err != nil { + return + } + err = unix.IoctlIfreq(int(fd), unix.TUNGETIFF, ifr) + if err != nil { + return + } + }); e != nil { + return e + } + return err +} + +// CreateTUN creates a Device with the provided name and MTU. +func CreateTUN(name string, mtu int) (Device, error) { + nfd, err := unix.Open(cloneDevicePath, unix.O_RDWR|unix.O_CLOEXEC, 0) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("CreateTUN(%q) failed; %s does not exist", name, cloneDevicePath) + } + return nil, err + } + + ifr, err := unix.NewIfreq(name) + if err != nil { + return nil, err + } + // IFF_VNET_HDR enables the "tun status hack" via routineHackListener() + // where a null write will return EINVAL indicating the TUN is up. + ifr.SetUint16(unix.IFF_TUN | unix.IFF_NO_PI | unix.IFF_VNET_HDR) + err = unix.IoctlIfreq(nfd, unix.TUNSETIFF, ifr) + if err != nil { + return nil, err + } + + err = unix.SetNonblock(nfd, true) + if err != nil { + unix.Close(nfd) + return nil, err + } + + // Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line. + + fd := os.NewFile(uintptr(nfd), cloneDevicePath) + return CreateTUNFromFile(fd, mtu) +} + +// CreateTUNFromFile creates a Device from an os.File with the provided MTU. +func CreateTUNFromFile(file *os.File, mtu int) (Device, error) { + tun := &NativeTun{ + tunFile: file, + events: make(chan Event, 5), + errors: make(chan error, 5), + statusListenersShutdown: make(chan struct{}), + } + + name, err := tun.Name() + if err != nil { + return nil, err + } + + err = tun.initFromFlags(name) + if err != nil { + return nil, err + } + + // start event listener + tun.index, err = getIFIndex(name) + if err != nil { + return nil, err + } + + tun.netlinkSock, err = createNetlinkSocket() + if err != nil { + return nil, err + } + tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) + if err != nil { + unix.Close(tun.netlinkSock) + return nil, err + } + + tun.hackListenerClosed.Lock() + go tun.routineNetlinkListener() + go tun.routineHackListener() // cross namespace + + err = tun.setMTU(mtu) + if err != nil { + unix.Close(tun.netlinkSock) + return nil, err + } + + return tun, nil +} + +// CreateUnmonitoredTUNFromFD creates a Device from the provided file +// descriptor. +func CreateUnmonitoredTUNFromFD(fd int) (Device, string, error) { + err := unix.SetNonblock(fd, true) + if err != nil { + return nil, "", err + } + file := os.NewFile(uintptr(fd), "/dev/tun") + tun := &NativeTun{ + tunFile: file, + events: make(chan Event, 5), + errors: make(chan error, 5), + } + name, err := tun.Name() + if err != nil { + return nil, "", err + } + err = tun.initFromFlags(name) + if err != nil { + return nil, "", err + } + return tun, name, err +} diff --git a/tun/tun_windows.go b/tun/tun_windows.go new file mode 100644 index 0000000..be216ce --- /dev/null +++ b/tun/tun_windows.go @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. + */ + +package tun + +import ( + "crypto/md5" + "errors" + "fmt" + "net/netip" + "os" + "sync" + "sync/atomic" + "time" + "unsafe" + _ "unsafe" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wintun" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" +) + +const ( + rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond) + spinloopRateThreshold = 800000000 / 8 // 800mbps + spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s +) + +type rateJuggler struct { + current atomic.Uint64 + nextByteCount atomic.Uint64 + nextStartTime atomic.Int64 + changing atomic.Bool +} + +type NativeTun struct { + wt *wintun.Adapter + name string + handle windows.Handle + rate rateJuggler + session wintun.Session + readWait windows.Handle + events chan Event + running sync.WaitGroup + closeOnce sync.Once + close atomic.Bool + mtu int +} + +var ( + WintunTunnelType = "vxlan" + WintunGUIDPrefix = "virtual-vxlan Windows GUID v1" +) + +//go:linkname procyield runtime.procyield +func procyield(cycles uint32) + +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +// Create an 128bit GUID using interface name. +func generateGUIDByDeviceName(name string) (*windows.GUID, error) { + hash := md5.New() + _, err := hash.Write([]byte(WintunGUIDPrefix + name)) + if err != nil { + return nil, err + } + sum := hash.Sum(nil) + + return (*windows.GUID)(unsafe.Pointer(&sum[0])), nil +} + +// CreateTUN creates a Wintun interface with the given name. Should a Wintun +// interface with the same name exist, it is reused. +func CreateTUN(ifname string, mtu int) (Device, error) { + guid, err := generateGUIDByDeviceName(ifname) + if err != nil { + return nil, err + } + return CreateTUNWithRequestedGUID(ifname, guid, mtu) +} + +// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and +// a requested GUID. Should a Wintun interface with the same name exist, it is reused. +func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int) (Device, error) { + wt, err := wintun.CreateAdapter(ifname, WintunTunnelType, requestedGUID) + if err != nil { + return nil, fmt.Errorf("error creating interface: %w", err) + } + + forcedMTU := 1420 + if mtu > 0 { + forcedMTU = mtu + } + + tun := &NativeTun{ + wt: wt, + name: ifname, + handle: windows.InvalidHandle, + events: make(chan Event, 10), + mtu: forcedMTU, + } + + err = tun.setMTU(windows.AF_INET, forcedMTU) + if err != nil { + wt.Close() + close(tun.events) + return nil, fmt.Errorf("error setting MTU for IPv4: %w", err) + } + err = tun.setMTU(windows.AF_INET6, forcedMTU) + if err != nil { + wt.Close() + close(tun.events) + return nil, fmt.Errorf("error setting MTU for IPv6: %w", err) + } + + tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB + if err != nil { + tun.wt.Close() + close(tun.events) + return nil, fmt.Errorf("error starting session: %w", err) + } + tun.readWait = tun.session.ReadWaitEvent() + return tun, nil +} + +func (tun *NativeTun) Name() (string, error) { + return tun.name, nil +} + +func (tun *NativeTun) File() *os.File { + return nil +} + +func (tun *NativeTun) Events() <-chan Event { + return tun.events +} + +func (tun *NativeTun) Close() error { + var err error + tun.closeOnce.Do(func() { + tun.close.Store(true) + windows.SetEvent(tun.readWait) + tun.running.Wait() + tun.session.End() + if tun.wt != nil { + tun.wt.Close() + } + close(tun.events) + }) + return err +} + +func (tun *NativeTun) setMTU(family winipcfg.AddressFamily, mtu int) error { + luid := winipcfg.LUID(tun.LUID()) + ipif, err := luid.IPInterface(family) + if err != nil { + return err + } + ipif.NLMTU = uint32(mtu) + return ipif.Set() +} + +func (tun *NativeTun) SetMTU(mtu int) error { + if tun.close.Load() { + return nil + } + err := tun.setMTU(windows.AF_INET, mtu) + if err != nil { + return fmt.Errorf("error setting MTU for IPv4: %v", err) + } + err = tun.setMTU(windows.AF_INET6, mtu) + if err != nil { + return fmt.Errorf("error setting MTU for IPv6: %v", err) + } + update := tun.mtu != mtu + tun.mtu = mtu + if update { + tun.events <- EventMTUUpdate + } + return nil +} + +func (tun *NativeTun) MTU() (int, error) { + return tun.mtu, nil +} + +// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking. + +func (tun *NativeTun) Read(b []byte) (int, error) { + tun.running.Add(1) + defer tun.running.Done() +retry: + if tun.close.Load() { + return 0, os.ErrClosed + } + start := nanotime() + shouldSpin := tun.rate.current.Load() >= spinloopRateThreshold && uint64(start-tun.rate.nextStartTime.Load()) <= rateMeasurementGranularity*2 + for { + if tun.close.Load() { + return 0, os.ErrClosed + } + packet, err := tun.session.ReceivePacket() + switch err { + case nil: + n := copy(b, packet) + tun.session.ReleaseReceivePacket(packet) + tun.rate.update(uint64(n)) + return n, nil + case windows.ERROR_NO_MORE_ITEMS: + if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration { + windows.WaitForSingleObject(tun.readWait, windows.INFINITE) + goto retry + } + procyield(1) + continue + case windows.ERROR_HANDLE_EOF: + return 0, os.ErrClosed + case windows.ERROR_INVALID_DATA: + return 0, errors.New("send ring corrupt") + } + return 0, fmt.Errorf("Read failed: %w", err) + } +} + +func (tun *NativeTun) Write(b []byte) (int, error) { + tun.running.Add(1) + defer tun.running.Done() + if tun.close.Load() { + return 0, os.ErrClosed + } + + packetSize := len(b) + tun.rate.update(uint64(packetSize)) + + packet, err := tun.session.AllocateSendPacket(packetSize) + switch err { + case nil: + // TODO: Explore options to eliminate this copy. + copy(packet, b) + tun.session.SendPacket(packet) + return packetSize, nil + case windows.ERROR_HANDLE_EOF: + return 0, os.ErrClosed + case windows.ERROR_BUFFER_OVERFLOW: + return 0, nil // Dropping when ring is full. + } + return 0, fmt.Errorf("Write failed: %w", err) +} + +// LUID returns Windows interface instance ID. +func (tun *NativeTun) LUID() uint64 { + tun.running.Add(1) + defer tun.running.Done() + if tun.close.Load() { + return 0 + } + return tun.wt.LUID() +} + +func (tun *NativeTun) SetIPAddresses(addresses []netip.Prefix) error { + luid := winipcfg.LUID(tun.LUID()) + + err := luid.SetIPAddresses(addresses) + if err != nil { + return fmt.Errorf("failed to set address: %w", err) + } + + return nil +} + +func addrFromSocketAddress(sockAddr windows.SocketAddress) netip.Addr { + ip := sockAddr.IP() + ip4 := ip.To4() + var addr netip.Addr + if ip4 != nil { + addr = netip.AddrFrom4([4]byte(ip4)) + } else { + addr = netip.AddrFrom16([16]byte(ip)) + } + return addr +} + +func (tun *NativeTun) GetIPAddresses() ([]netip.Prefix, error) { + luid := winipcfg.LUID(tun.LUID()) + ipAdaters, err := winipcfg.GetAdaptersAddresses(windows.AF_UNSPEC, winipcfg.GAAFlagIncludePrefix|winipcfg.GAAFlagSkipAnycast|winipcfg.GAAFlagSkipMulticast|winipcfg.GAAFlagSkipDNSServer|winipcfg.GAAFlagSkipFriendlyName|winipcfg.GAAFlagSkipDNSInfo) + if err != nil { + return nil, fmt.Errorf("failed to get IP adapters: %w", err) + } + var prefixes []netip.Prefix + for _, ipAdater := range ipAdaters { + if ipAdater.LUID == luid { + unicast := ipAdater.FirstUnicastAddress + for unicast != nil { + addr := addrFromSocketAddress(unicast.Address) + prefix := ipAdater.FirstPrefix + for prefix != nil { + pAddr := addrFromSocketAddress(prefix.Address) + if (pAddr.Is4() && prefix.PrefixLength != 32) || (pAddr.Is6() && prefix.PrefixLength != 128) { + nPrefix := netip.PrefixFrom(pAddr, int(prefix.PrefixLength)) + if nPrefix.Contains(addr) { + prefixes = append(prefixes, netip.PrefixFrom(addr, int(prefix.PrefixLength))) + prefix = nil + continue + } + } + prefix = prefix.Next + } + unicast = unicast.Next + } + } + } + return prefixes, nil +} + +// RunningVersion returns the running version of the Wintun driver. +func (tun *NativeTun) RunningVersion() (version uint32, err error) { + return wintun.RunningVersion() +} + +func (rate *rateJuggler) update(packetLen uint64) { + now := nanotime() + total := rate.nextByteCount.Add(packetLen) + period := uint64(now - rate.nextStartTime.Load()) + if period >= rateMeasurementGranularity { + if !rate.changing.CompareAndSwap(false, true) { + return + } + rate.nextStartTime.Store(now) + rate.current.Store(total * uint64(time.Second/time.Nanosecond) / period) + rate.nextByteCount.Store(0) + rate.changing.Store(false) + } +} diff --git a/update_cmd.go b/update_cmd.go new file mode 100644 index 0000000..adc8417 --- /dev/null +++ b/update_cmd.go @@ -0,0 +1,10 @@ +package main + +// Command to manage this service. +type UpdateCmd struct{} + +func (s *UpdateCmd) Run() (err error) { + config := ReadMinimalConfig() + CheckForUpdate(config.Update, false) + return +} diff --git a/updater.go b/updater.go new file mode 100644 index 0000000..a037a90 --- /dev/null +++ b/updater.go @@ -0,0 +1,262 @@ +package main + +import ( + "bufio" + "context" + "errors" + "fmt" + "math/rand" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/coreos/go-systemd/daemon" + "github.com/creativeprojects/go-selfupdate" + "github.com/hashicorp/go-version" + log "github.com/sirupsen/logrus" +) + +// Check for update and update if one is available. +func Update(c *UpdateConfig) error { + log.Println("Checking for update.") + // Setup source. + source, err := selfupdate.NewGitHubSource(selfupdate.GitHubConfig{}) + if err != nil { + return err + } + + // Get the path to ourself. + exe, err := selfupdate.ExecutablePath() + if err != nil { + return fmt.Errorf("could not locate executable path: %s", err) + } + updateDir, cmd := filepath.Split(exe) + oldSavePath := filepath.Join(updateDir, fmt.Sprintf(".%s.old", cmd)) + + // Get updater with source and validator. + updater, err := selfupdate.NewUpdater(selfupdate.Config{ + Source: source, + Validator: &selfupdate.ChecksumValidator{UniqueFilename: "checksums.txt"}, + OldSavePath: oldSavePath, + }) + if err != nil { + return err + } + + // Find the latest release. + release, found, err := updater.DetectLatest(context.Background(), selfupdate.NewRepositorySlug(c.Owner, c.Repo)) + if err != nil { + return err + } + if !found { + log.Println("No updates available.") + return nil + } + + // Compare the versions. + thisVersion, err := version.NewVersion(c.CurrentVersion) + if err != nil { + return err + } + latestVersion, err := version.NewVersion(release.Version()) + if err != nil { + return err + } + + // If an update isn't available, end. + if !thisVersion.LessThan(latestVersion) { + log.Println("No updates available.") + return nil + } + log.Println("Updating to version:", release.Version()) + + // We're updating, tell the app so services can be stopped. + c.PreUpdate() + + // Perform the update. + err = updater.UpdateTo(context.Background(), release, exe) + + // If update failed, rollback and tell the app we failed. +abortUpdate: + if err != nil { + rerr := os.Rename(oldSavePath, exe) + if rerr != nil { + log.Println("Failed to rollback update:", rerr) + } + c.AbortUpdate() + return err + } + log.Println("Updated.") + + // If relaunch is requested, then start the new exe and confirm it works. + if c.ShouldRelaunch { + // Make process and get its stdout/stderr. + log.Println("Starting new process.") + p := exec.Command(exe, os.Args[1:]...) + p.Env = os.Environ() + p.Env = append(p.Env, "UPDATER_UPDATE=1") + stdout, err := p.StdoutPipe() + if err != nil { + goto abortUpdate + } + stderr, err := p.StderrPipe() + if err != nil { + goto abortUpdate + } + + // Start process. + err = p.Start() + if err != nil { + goto abortUpdate + } + + // Set timeout to kill process. + timer := time.AfterFunc(c.StartupTimeout, func() { + p.Process.Kill() + }) + + // Channels to confirm success or failure. + success := make(chan bool) + failure := make(chan error) + + // Scan the stdout for success message. + go func() { + stdoutScanner := bufio.NewScanner(stdout) + for stdoutScanner.Scan() { + line := stdoutScanner.Text() + fmt.Fprintln(os.Stdout, line) + if c.IsSuccessMsg(line) { + success <- true + return + } + } + }() + + // Scan the stderr for sucess message. + go func() { + stderrScanner := bufio.NewScanner(stderr) + for stderrScanner.Scan() { + line := stderrScanner.Text() + fmt.Fprintln(os.Stderr, line) + if c.IsSuccessMsg(line) { + success <- true + return + } + } + }() + + // Wait for command exit, and pass errors. + go func() { + err := p.Wait() + failure <- err + }() + + // Wait for one of the channels to be sent information. + select { + case <-success: + case err = <-failure: + if err == nil { + err = errors.New("program exited without error") + } + } + + // Stop the timer as the process is either going to be left running or is stopped. + timer.Stop() + + // If stop due to error, abort. + if err != nil { + goto abortUpdate + } + + // The update was successful, so we can remove the old binary. + os.Remove(oldSavePath) + + // Set the new process stdio to ours. + p.Stdout = os.Stdout + p.Stderr = os.Stderr + p.Stdin = os.Stdin + + // Notify systemd to watch the new process. + // Those babysitting fees are too high to have it watch us too. + // Amy said SystemD doesn't do babysitting. + daemon.SdNotify(false, fmt.Sprintf("MAINPID=%d", p.Process.Pid)) + + // Quit this process as the new process will continue running as a fork. + os.Exit(0) + } + + // The update was successful, so we can remove the old binary. + os.Remove(oldSavePath) + + return nil +} + +// Check for updates, and apply. +func CheckForUpdate(c *UpdateConfig, relaunch bool) { + // Set update config local variables. + c.CurrentVersion = serviceVersion + c.ShouldRelaunch = relaunch + c.PreUpdate = func() { + // Stop all listeners to allow updated service to start. + for len(app.Net.Listeners) >= 1 { + app.Net.Listeners[0].Close() + } + + // Stop the grpc server. + if app.grpcServer != nil { + app.grpcServer.Close() + } + } + c.IsSuccessMsg = func(msg string) bool { + if strings.Contains(msg, "Service started.") { + return true + } + return false + } + c.StartupTimeout = 5 * time.Minute + // If update is aborted, we should restart the service. + c.AbortUpdate = func() { + // Read the configuration from file. + config := ReadConfig() + + // Start the GRPC server for cli communication. + _, err := NewGRPCServer(config.RPCPath) + if err != nil { + log.Fatalln(err) + } + + // Apply the configuration read. + err = ApplyConfig(config) + // If error applying the config, we should fail. + if err != nil { + log.Fatalln(err) + } + } + err := Update(c) + if err != nil { + log.Println("Failure checking for update:", err) + } +} + +// Every 24 hours, check for updates. +func (a *App) RunUpdateLoop() { + // If disabled, don't run loop. + if app.UpdateConfig.Disabled { + return + } + + // Randomly check for updates at first start. + if os.Getenv("UPDATER_UPDATE") != "1" && rand.Intn(20) == 2 { + CheckForUpdate(app.UpdateConfig, true) + } + + // Run update check every 24 hours. + for { + nextUpdate := time.Hour * 24 + nextUpdate += time.Duration(rand.Intn(18000)) * time.Second + time.Sleep(nextUpdate) + CheckForUpdate(app.UpdateConfig, true) + } +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..3ce2133 --- /dev/null +++ b/utils.go @@ -0,0 +1,20 @@ +package main + +import ( + "crypto/rand" + "net" +) + +// Generate an random MAC in the locally administered OUI. +func generateRandomMAC() net.HardwareAddr { + // Start a new MAC address. + mac := make(net.HardwareAddr, 6) + + // Just replace all bytes in MAC with random bytes. + rand.Read(mac) + // Set OUI to locally administered space. + mac[0] = 0x0a + mac[1] = 0x00 + + return mac +} diff --git a/vxlan/vxlan.pb.go b/vxlan/vxlan.pb.go new file mode 100644 index 0000000..d1f32df --- /dev/null +++ b/vxlan/vxlan.pb.go @@ -0,0 +1,1743 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.1 +// protoc v5.29.2 +// source: vxlan/vxlan.proto + +package vxlan + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Standard empty message. +type Empty struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Empty) Reset() { + *x = Empty{} + mi := &file_vxlan_vxlan_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{0} +} + +// Listener messages. +type ListenerRequestWithName struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListenerRequestWithName) Reset() { + *x = ListenerRequestWithName{} + mi := &file_vxlan_vxlan_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListenerRequestWithName) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListenerRequestWithName) ProtoMessage() {} + +func (x *ListenerRequestWithName) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListenerRequestWithName.ProtoReflect.Descriptor instead. +func (*ListenerRequestWithName) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{1} +} + +func (x *ListenerRequestWithName) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type Listener struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + MaxMessageSize int32 `protobuf:"varint,3,opt,name=maxMessageSize,proto3" json:"maxMessageSize,omitempty"` + Permanent bool `protobuf:"varint,4,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Listener) Reset() { + *x = Listener{} + mi := &file_vxlan_vxlan_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Listener) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Listener) ProtoMessage() {} + +func (x *Listener) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Listener.ProtoReflect.Descriptor instead. +func (*Listener) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{2} +} + +func (x *Listener) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Listener) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Listener) GetMaxMessageSize() int32 { + if x != nil { + return x.MaxMessageSize + } + return 0 +} + +func (x *Listener) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type ListListenersReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Listeners []*Listener `protobuf:"bytes,1,rep,name=listeners,proto3" json:"listeners,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListListenersReply) Reset() { + *x = ListListenersReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListListenersReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListListenersReply) ProtoMessage() {} + +func (x *ListListenersReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListListenersReply.ProtoReflect.Descriptor instead. +func (*ListListenersReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{3} +} + +func (x *ListListenersReply) GetListeners() []*Listener { + if x != nil { + return x.Listeners + } + return nil +} + +type ListenerMaxMessageSizeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Size int32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListenerMaxMessageSizeRequest) Reset() { + *x = ListenerMaxMessageSizeRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListenerMaxMessageSizeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListenerMaxMessageSizeRequest) ProtoMessage() {} + +func (x *ListenerMaxMessageSizeRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListenerMaxMessageSizeRequest.ProtoReflect.Descriptor instead. +func (*ListenerMaxMessageSizeRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{4} +} + +func (x *ListenerMaxMessageSizeRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ListenerMaxMessageSizeRequest) GetSize() int32 { + if x != nil { + return x.Size + } + return 0 +} + +type ListenerMaxMessageSizeReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Size int32 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListenerMaxMessageSizeReply) Reset() { + *x = ListenerMaxMessageSizeReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListenerMaxMessageSizeReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListenerMaxMessageSizeReply) ProtoMessage() {} + +func (x *ListenerMaxMessageSizeReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListenerMaxMessageSizeReply.ProtoReflect.Descriptor instead. +func (*ListenerMaxMessageSizeReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{5} +} + +func (x *ListenerMaxMessageSizeReply) GetSize() int32 { + if x != nil { + return x.Size + } + return 0 +} + +// Interface messages. +type Interface struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Vni uint32 `protobuf:"varint,2,opt,name=vni,proto3" json:"vni,omitempty"` + Mtu int32 `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"` + Permanent bool `protobuf:"varint,4,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Interface) Reset() { + *x = Interface{} + mi := &file_vxlan_vxlan_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Interface) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Interface) ProtoMessage() {} + +func (x *Interface) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Interface.ProtoReflect.Descriptor instead. +func (*Interface) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{6} +} + +func (x *Interface) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Interface) GetVni() uint32 { + if x != nil { + return x.Vni + } + return 0 +} + +func (x *Interface) GetMtu() int32 { + if x != nil { + return x.Mtu + } + return 0 +} + +func (x *Interface) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type ListInterfacesReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Interfaces []*Interface `protobuf:"bytes,1,rep,name=interfaces,proto3" json:"interfaces,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListInterfacesReply) Reset() { + *x = ListInterfacesReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListInterfacesReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListInterfacesReply) ProtoMessage() {} + +func (x *ListInterfacesReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListInterfacesReply.ProtoReflect.Descriptor instead. +func (*ListInterfacesReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{7} +} + +func (x *ListInterfacesReply) GetInterfaces() []*Interface { + if x != nil { + return x.Interfaces + } + return nil +} + +type AddInterfaceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Vni uint32 `protobuf:"varint,3,opt,name=vni,proto3" json:"vni,omitempty"` + Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` + Permanent bool `protobuf:"varint,5,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddInterfaceRequest) Reset() { + *x = AddInterfaceRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddInterfaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddInterfaceRequest) ProtoMessage() {} + +func (x *AddInterfaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddInterfaceRequest.ProtoReflect.Descriptor instead. +func (*AddInterfaceRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{8} +} + +func (x *AddInterfaceRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *AddInterfaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AddInterfaceRequest) GetVni() uint32 { + if x != nil { + return x.Vni + } + return 0 +} + +func (x *AddInterfaceRequest) GetMtu() int32 { + if x != nil { + return x.Mtu + } + return 0 +} + +func (x *AddInterfaceRequest) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type InterfaceRequestWithName struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceRequestWithName) Reset() { + *x = InterfaceRequestWithName{} + mi := &file_vxlan_vxlan_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceRequestWithName) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceRequestWithName) ProtoMessage() {} + +func (x *InterfaceRequestWithName) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceRequestWithName.ProtoReflect.Descriptor instead. +func (*InterfaceRequestWithName) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{9} +} + +func (x *InterfaceRequestWithName) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceRequestWithName) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type InterfaceMTURequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Mtu int32 `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceMTURequest) Reset() { + *x = InterfaceMTURequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceMTURequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceMTURequest) ProtoMessage() {} + +func (x *InterfaceMTURequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceMTURequest.ProtoReflect.Descriptor instead. +func (*InterfaceMTURequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{10} +} + +func (x *InterfaceMTURequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceMTURequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceMTURequest) GetMtu() int32 { + if x != nil { + return x.Mtu + } + return 0 +} + +type InterfaceMTUReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mtu int32 `protobuf:"varint,1,opt,name=mtu,proto3" json:"mtu,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceMTUReply) Reset() { + *x = InterfaceMTUReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceMTUReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceMTUReply) ProtoMessage() {} + +func (x *InterfaceMTUReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceMTUReply.ProtoReflect.Descriptor instead. +func (*InterfaceMTUReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{11} +} + +func (x *InterfaceMTUReply) GetMtu() int32 { + if x != nil { + return x.Mtu + } + return 0 +} + +type InterfaceMACAddressRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Mac string `protobuf:"bytes,3,opt,name=mac,proto3" json:"mac,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceMACAddressRequest) Reset() { + *x = InterfaceMACAddressRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceMACAddressRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceMACAddressRequest) ProtoMessage() {} + +func (x *InterfaceMACAddressRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceMACAddressRequest.ProtoReflect.Descriptor instead. +func (*InterfaceMACAddressRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{12} +} + +func (x *InterfaceMACAddressRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceMACAddressRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceMACAddressRequest) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +type InterfaceMACAddressReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mac string `protobuf:"bytes,1,opt,name=mac,proto3" json:"mac,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceMACAddressReply) Reset() { + *x = InterfaceMACAddressReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceMACAddressReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceMACAddressReply) ProtoMessage() {} + +func (x *InterfaceMACAddressReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceMACAddressReply.ProtoReflect.Descriptor instead. +func (*InterfaceMACAddressReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{13} +} + +func (x *InterfaceMACAddressReply) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +type InterfaceIPAddressesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + IpAddress []string `protobuf:"bytes,3,rep,name=ipAddress,proto3" json:"ipAddress,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceIPAddressesRequest) Reset() { + *x = InterfaceIPAddressesRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceIPAddressesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceIPAddressesRequest) ProtoMessage() {} + +func (x *InterfaceIPAddressesRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceIPAddressesRequest.ProtoReflect.Descriptor instead. +func (*InterfaceIPAddressesRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{14} +} + +func (x *InterfaceIPAddressesRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceIPAddressesRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceIPAddressesRequest) GetIpAddress() []string { + if x != nil { + return x.IpAddress + } + return nil +} + +type InterfaceIPAddressesReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + IpAddress []string `protobuf:"bytes,1,rep,name=ipAddress,proto3" json:"ipAddress,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceIPAddressesReply) Reset() { + *x = InterfaceIPAddressesReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceIPAddressesReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceIPAddressesReply) ProtoMessage() {} + +func (x *InterfaceIPAddressesReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceIPAddressesReply.ProtoReflect.Descriptor instead. +func (*InterfaceIPAddressesReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{15} +} + +func (x *InterfaceIPAddressesReply) GetIpAddress() []string { + if x != nil { + return x.IpAddress + } + return nil +} + +type InterfaceMacEntryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Mac string `protobuf:"bytes,3,opt,name=mac,proto3" json:"mac,omitempty"` + Destination string `protobuf:"bytes,4,opt,name=destination,proto3" json:"destination,omitempty"` + Permanent bool `protobuf:"varint,5,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceMacEntryRequest) Reset() { + *x = InterfaceMacEntryRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceMacEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceMacEntryRequest) ProtoMessage() {} + +func (x *InterfaceMacEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceMacEntryRequest.ProtoReflect.Descriptor instead. +func (*InterfaceMacEntryRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{16} +} + +func (x *InterfaceMacEntryRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceMacEntryRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceMacEntryRequest) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *InterfaceMacEntryRequest) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *InterfaceMacEntryRequest) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type InterfaceRemoveMacEntryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Mac string `protobuf:"bytes,3,opt,name=mac,proto3" json:"mac,omitempty"` + Destination string `protobuf:"bytes,4,opt,name=destination,proto3" json:"destination,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceRemoveMacEntryRequest) Reset() { + *x = InterfaceRemoveMacEntryRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceRemoveMacEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceRemoveMacEntryRequest) ProtoMessage() {} + +func (x *InterfaceRemoveMacEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceRemoveMacEntryRequest.ProtoReflect.Descriptor instead. +func (*InterfaceRemoveMacEntryRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{17} +} + +func (x *InterfaceRemoveMacEntryRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceRemoveMacEntryRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceRemoveMacEntryRequest) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *InterfaceRemoveMacEntryRequest) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +type MacEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mac string `protobuf:"bytes,1,opt,name=mac,proto3" json:"mac,omitempty"` + Destination string `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` + Permanent bool `protobuf:"varint,3,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MacEntry) Reset() { + *x = MacEntry{} + mi := &file_vxlan_vxlan_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MacEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MacEntry) ProtoMessage() {} + +func (x *MacEntry) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MacEntry.ProtoReflect.Descriptor instead. +func (*MacEntry) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{18} +} + +func (x *MacEntry) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *MacEntry) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *MacEntry) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type InterfaceMacEntryReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entries []*MacEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceMacEntryReply) Reset() { + *x = InterfaceMacEntryReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceMacEntryReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceMacEntryReply) ProtoMessage() {} + +func (x *InterfaceMacEntryReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceMacEntryReply.ProtoReflect.Descriptor instead. +func (*InterfaceMacEntryReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{19} +} + +func (x *InterfaceMacEntryReply) GetEntries() []*MacEntry { + if x != nil { + return x.Entries + } + return nil +} + +type InterfaceARPEntryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + Mac string `protobuf:"bytes,4,opt,name=mac,proto3" json:"mac,omitempty"` + Permanent bool `protobuf:"varint,5,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceARPEntryRequest) Reset() { + *x = InterfaceARPEntryRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceARPEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceARPEntryRequest) ProtoMessage() {} + +func (x *InterfaceARPEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceARPEntryRequest.ProtoReflect.Descriptor instead. +func (*InterfaceARPEntryRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{20} +} + +func (x *InterfaceARPEntryRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceARPEntryRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceARPEntryRequest) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *InterfaceARPEntryRequest) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *InterfaceARPEntryRequest) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type InterfaceRemoveARPEntryRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ListenerName string `protobuf:"bytes,1,opt,name=listenerName,proto3" json:"listenerName,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceRemoveARPEntryRequest) Reset() { + *x = InterfaceRemoveARPEntryRequest{} + mi := &file_vxlan_vxlan_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceRemoveARPEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceRemoveARPEntryRequest) ProtoMessage() {} + +func (x *InterfaceRemoveARPEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceRemoveARPEntryRequest.ProtoReflect.Descriptor instead. +func (*InterfaceRemoveARPEntryRequest) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{21} +} + +func (x *InterfaceRemoveARPEntryRequest) GetListenerName() string { + if x != nil { + return x.ListenerName + } + return "" +} + +func (x *InterfaceRemoveARPEntryRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InterfaceRemoveARPEntryRequest) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type ArpEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Mac string `protobuf:"bytes,2,opt,name=mac,proto3" json:"mac,omitempty"` + Expires string `protobuf:"bytes,3,opt,name=expires,proto3" json:"expires,omitempty"` + Permanent bool `protobuf:"varint,4,opt,name=permanent,proto3" json:"permanent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ArpEntry) Reset() { + *x = ArpEntry{} + mi := &file_vxlan_vxlan_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ArpEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ArpEntry) ProtoMessage() {} + +func (x *ArpEntry) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ArpEntry.ProtoReflect.Descriptor instead. +func (*ArpEntry) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{22} +} + +func (x *ArpEntry) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *ArpEntry) GetMac() string { + if x != nil { + return x.Mac + } + return "" +} + +func (x *ArpEntry) GetExpires() string { + if x != nil { + return x.Expires + } + return "" +} + +func (x *ArpEntry) GetPermanent() bool { + if x != nil { + return x.Permanent + } + return false +} + +type InterfaceArpEntryReply struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entries []*ArpEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InterfaceArpEntryReply) Reset() { + *x = InterfaceArpEntryReply{} + mi := &file_vxlan_vxlan_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InterfaceArpEntryReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InterfaceArpEntryReply) ProtoMessage() {} + +func (x *InterfaceArpEntryReply) ProtoReflect() protoreflect.Message { + mi := &file_vxlan_vxlan_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InterfaceArpEntryReply.ProtoReflect.Descriptor instead. +func (*InterfaceArpEntryReply) Descriptor() ([]byte, []int) { + return file_vxlan_vxlan_proto_rawDescGZIP(), []int{23} +} + +func (x *InterfaceArpEntryReply) GetEntries() []*ArpEntry { + if x != nil { + return x.Entries + } + return nil +} + +var File_vxlan_vxlan_proto protoreflect.FileDescriptor + +var file_vxlan_vxlan_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2f, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x7e, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0e, + 0x6d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, + 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x6c, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x78, + 0x6c, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x6c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x47, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x65, 0x72, 0x4d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x22, 0x31, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x61, 0x78, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x22, 0x61, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x6e, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x03, 0x76, 0x6e, 0x69, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x72, + 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x47, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, + 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x22, + 0x8f, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x76, 0x6e, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x76, 0x6e, + 0x69, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, + 0x6d, 0x74, 0x75, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, + 0x74, 0x22, 0x52, 0x0a, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, + 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5f, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x4d, 0x54, 0x55, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0c, + 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x22, 0x25, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x4d, 0x54, 0x55, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, + 0x74, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x22, 0x66, 0x0a, + 0x1a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x41, 0x43, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6d, 0x61, 0x63, 0x22, 0x2c, 0x0a, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x4d, 0x41, 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6d, 0x61, 0x63, 0x22, 0x73, 0x0a, 0x1b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x70, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x69, + 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x39, 0x0a, 0x19, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x4d, 0x61, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x22, 0x0a, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, + 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x1e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x61, + 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5c, 0x0a, 0x08, 0x4d, 0x61, 0x63, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, + 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, + 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x16, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4d, 0x61, 0x63, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x9c, 0x01, 0x0a, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x52, 0x50, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x6c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, + 0x61, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x1c, 0x0a, + 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x72, 0x0a, 0x1e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x52, + 0x50, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, + 0x6e, 0x0a, 0x08, 0x41, 0x72, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, + 0x43, 0x0a, 0x16, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x72, 0x70, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x78, 0x6c, + 0x61, 0x6e, 0x2e, 0x41, 0x72, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x32, 0x88, 0x0e, 0x0a, 0x05, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x12, 0x2a, + 0x0a, 0x0a, 0x53, 0x61, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0c, 0x2e, 0x76, + 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, + 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2c, 0x0a, 0x0c, 0x52, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0c, 0x2e, 0x76, 0x78, 0x6c, + 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, + 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, + 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x65, 0x72, 0x12, 0x0f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, + 0x69, 0x7a, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, + 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x19, 0x47, 0x65, 0x74, + 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x22, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4d, 0x61, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0e, + 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1e, + 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x1a, + 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, + 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x2e, 0x76, + 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x76, 0x78, + 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x0c, 0x2e, 0x76, + 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x0f, + 0x53, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x54, 0x55, 0x12, + 0x1a, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x4d, 0x54, 0x55, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, + 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x54, 0x55, 0x12, 0x1f, + 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, + 0x18, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x4d, 0x54, 0x55, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x16, 0x53, + 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x41, 0x43, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x41, 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x41, 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, + 0x61, 0x6d, 0x65, 0x1a, 0x1f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x41, 0x43, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x12, 0x22, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x12, 0x1f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, + 0x65, 0x1a, 0x20, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x49, 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x14, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x41, 0x43, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x2e, + 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, + 0x61, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, + 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x50, + 0x0a, 0x17, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x4d, 0x41, 0x43, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x2e, 0x76, 0x78, 0x6c, 0x61, + 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x4d, 0x61, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x5a, 0x0a, 0x16, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x47, 0x65, 0x74, + 0x4d, 0x41, 0x43, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x76, 0x78, 0x6c, + 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x1d, 0x2e, 0x76, 0x78, + 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x63, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x16, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x4d, 0x41, + 0x43, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, + 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x1a, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x41, 0x64, 0x64, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x52, 0x50, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x52, 0x50, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x17, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x52, 0x50, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x25, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x52, 0x50, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x16, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x47, 0x65, 0x74, 0x41, 0x52, 0x50, 0x45, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, + 0x61, 0x6d, 0x65, 0x1a, 0x1d, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x41, 0x72, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x16, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x41, 0x52, 0x50, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1f, + 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x1a, + 0x0c, 0x2e, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, + 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, + 0x6d, 0x72, 0x67, 0x65, 0x63, 0x6b, 0x6f, 0x2f, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x2d, + 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x2f, 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_vxlan_vxlan_proto_rawDescOnce sync.Once + file_vxlan_vxlan_proto_rawDescData = file_vxlan_vxlan_proto_rawDesc +) + +func file_vxlan_vxlan_proto_rawDescGZIP() []byte { + file_vxlan_vxlan_proto_rawDescOnce.Do(func() { + file_vxlan_vxlan_proto_rawDescData = protoimpl.X.CompressGZIP(file_vxlan_vxlan_proto_rawDescData) + }) + return file_vxlan_vxlan_proto_rawDescData +} + +var file_vxlan_vxlan_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_vxlan_vxlan_proto_goTypes = []any{ + (*Empty)(nil), // 0: vxlan.Empty + (*ListenerRequestWithName)(nil), // 1: vxlan.ListenerRequestWithName + (*Listener)(nil), // 2: vxlan.Listener + (*ListListenersReply)(nil), // 3: vxlan.ListListenersReply + (*ListenerMaxMessageSizeRequest)(nil), // 4: vxlan.ListenerMaxMessageSizeRequest + (*ListenerMaxMessageSizeReply)(nil), // 5: vxlan.ListenerMaxMessageSizeReply + (*Interface)(nil), // 6: vxlan.Interface + (*ListInterfacesReply)(nil), // 7: vxlan.ListInterfacesReply + (*AddInterfaceRequest)(nil), // 8: vxlan.AddInterfaceRequest + (*InterfaceRequestWithName)(nil), // 9: vxlan.InterfaceRequestWithName + (*InterfaceMTURequest)(nil), // 10: vxlan.InterfaceMTURequest + (*InterfaceMTUReply)(nil), // 11: vxlan.InterfaceMTUReply + (*InterfaceMACAddressRequest)(nil), // 12: vxlan.InterfaceMACAddressRequest + (*InterfaceMACAddressReply)(nil), // 13: vxlan.InterfaceMACAddressReply + (*InterfaceIPAddressesRequest)(nil), // 14: vxlan.InterfaceIPAddressesRequest + (*InterfaceIPAddressesReply)(nil), // 15: vxlan.InterfaceIPAddressesReply + (*InterfaceMacEntryRequest)(nil), // 16: vxlan.InterfaceMacEntryRequest + (*InterfaceRemoveMacEntryRequest)(nil), // 17: vxlan.InterfaceRemoveMacEntryRequest + (*MacEntry)(nil), // 18: vxlan.MacEntry + (*InterfaceMacEntryReply)(nil), // 19: vxlan.InterfaceMacEntryReply + (*InterfaceARPEntryRequest)(nil), // 20: vxlan.InterfaceARPEntryRequest + (*InterfaceRemoveARPEntryRequest)(nil), // 21: vxlan.InterfaceRemoveARPEntryRequest + (*ArpEntry)(nil), // 22: vxlan.ArpEntry + (*InterfaceArpEntryReply)(nil), // 23: vxlan.InterfaceArpEntryReply +} +var file_vxlan_vxlan_proto_depIdxs = []int32{ + 2, // 0: vxlan.ListListenersReply.listeners:type_name -> vxlan.Listener + 6, // 1: vxlan.ListInterfacesReply.interfaces:type_name -> vxlan.Interface + 18, // 2: vxlan.InterfaceMacEntryReply.entries:type_name -> vxlan.MacEntry + 22, // 3: vxlan.InterfaceArpEntryReply.entries:type_name -> vxlan.ArpEntry + 0, // 4: vxlan.vxlan.SaveConfig:input_type -> vxlan.Empty + 0, // 5: vxlan.vxlan.ReloadConfig:input_type -> vxlan.Empty + 0, // 6: vxlan.vxlan.ListListeners:input_type -> vxlan.Empty + 2, // 7: vxlan.vxlan.AddListener:input_type -> vxlan.Listener + 1, // 8: vxlan.vxlan.RemoveListener:input_type -> vxlan.ListenerRequestWithName + 4, // 9: vxlan.vxlan.SetListenerMaxMessageSize:input_type -> vxlan.ListenerMaxMessageSizeRequest + 1, // 10: vxlan.vxlan.GetListenerMaxMessageSize:input_type -> vxlan.ListenerRequestWithName + 1, // 11: vxlan.vxlan.ListInterfaces:input_type -> vxlan.ListenerRequestWithName + 8, // 12: vxlan.vxlan.AddInterface:input_type -> vxlan.AddInterfaceRequest + 9, // 13: vxlan.vxlan.RemoveInterface:input_type -> vxlan.InterfaceRequestWithName + 10, // 14: vxlan.vxlan.SetInterfaceMTU:input_type -> vxlan.InterfaceMTURequest + 9, // 15: vxlan.vxlan.GetInterfaceMTU:input_type -> vxlan.InterfaceRequestWithName + 12, // 16: vxlan.vxlan.SetInterfaceMACAddress:input_type -> vxlan.InterfaceMACAddressRequest + 9, // 17: vxlan.vxlan.GetInterfaceMACAddress:input_type -> vxlan.InterfaceRequestWithName + 14, // 18: vxlan.vxlan.SetInterfaceIPAddresses:input_type -> vxlan.InterfaceIPAddressesRequest + 9, // 19: vxlan.vxlan.GetInterfaceIPAddresses:input_type -> vxlan.InterfaceRequestWithName + 16, // 20: vxlan.vxlan.InterfaceAddMACEntry:input_type -> vxlan.InterfaceMacEntryRequest + 17, // 21: vxlan.vxlan.InterfaceRemoveMACEntry:input_type -> vxlan.InterfaceRemoveMacEntryRequest + 9, // 22: vxlan.vxlan.InterfaceGetMACEntries:input_type -> vxlan.InterfaceRequestWithName + 9, // 23: vxlan.vxlan.InterfaceFlushMACTable:input_type -> vxlan.InterfaceRequestWithName + 20, // 24: vxlan.vxlan.InterfaceAddStaticARPEntry:input_type -> vxlan.InterfaceARPEntryRequest + 21, // 25: vxlan.vxlan.InterfaceRemoveARPEntry:input_type -> vxlan.InterfaceRemoveARPEntryRequest + 9, // 26: vxlan.vxlan.InterfaceGetARPEntries:input_type -> vxlan.InterfaceRequestWithName + 9, // 27: vxlan.vxlan.InterfaceFlushARPTable:input_type -> vxlan.InterfaceRequestWithName + 0, // 28: vxlan.vxlan.SaveConfig:output_type -> vxlan.Empty + 0, // 29: vxlan.vxlan.ReloadConfig:output_type -> vxlan.Empty + 3, // 30: vxlan.vxlan.ListListeners:output_type -> vxlan.ListListenersReply + 0, // 31: vxlan.vxlan.AddListener:output_type -> vxlan.Empty + 0, // 32: vxlan.vxlan.RemoveListener:output_type -> vxlan.Empty + 0, // 33: vxlan.vxlan.SetListenerMaxMessageSize:output_type -> vxlan.Empty + 5, // 34: vxlan.vxlan.GetListenerMaxMessageSize:output_type -> vxlan.ListenerMaxMessageSizeReply + 7, // 35: vxlan.vxlan.ListInterfaces:output_type -> vxlan.ListInterfacesReply + 0, // 36: vxlan.vxlan.AddInterface:output_type -> vxlan.Empty + 0, // 37: vxlan.vxlan.RemoveInterface:output_type -> vxlan.Empty + 0, // 38: vxlan.vxlan.SetInterfaceMTU:output_type -> vxlan.Empty + 11, // 39: vxlan.vxlan.GetInterfaceMTU:output_type -> vxlan.InterfaceMTUReply + 0, // 40: vxlan.vxlan.SetInterfaceMACAddress:output_type -> vxlan.Empty + 13, // 41: vxlan.vxlan.GetInterfaceMACAddress:output_type -> vxlan.InterfaceMACAddressReply + 0, // 42: vxlan.vxlan.SetInterfaceIPAddresses:output_type -> vxlan.Empty + 15, // 43: vxlan.vxlan.GetInterfaceIPAddresses:output_type -> vxlan.InterfaceIPAddressesReply + 0, // 44: vxlan.vxlan.InterfaceAddMACEntry:output_type -> vxlan.Empty + 0, // 45: vxlan.vxlan.InterfaceRemoveMACEntry:output_type -> vxlan.Empty + 19, // 46: vxlan.vxlan.InterfaceGetMACEntries:output_type -> vxlan.InterfaceMacEntryReply + 0, // 47: vxlan.vxlan.InterfaceFlushMACTable:output_type -> vxlan.Empty + 0, // 48: vxlan.vxlan.InterfaceAddStaticARPEntry:output_type -> vxlan.Empty + 0, // 49: vxlan.vxlan.InterfaceRemoveARPEntry:output_type -> vxlan.Empty + 23, // 50: vxlan.vxlan.InterfaceGetARPEntries:output_type -> vxlan.InterfaceArpEntryReply + 0, // 51: vxlan.vxlan.InterfaceFlushARPTable:output_type -> vxlan.Empty + 28, // [28:52] is the sub-list for method output_type + 4, // [4:28] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_vxlan_vxlan_proto_init() } +func file_vxlan_vxlan_proto_init() { + if File_vxlan_vxlan_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_vxlan_vxlan_proto_rawDesc, + NumEnums: 0, + NumMessages: 24, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_vxlan_vxlan_proto_goTypes, + DependencyIndexes: file_vxlan_vxlan_proto_depIdxs, + MessageInfos: file_vxlan_vxlan_proto_msgTypes, + }.Build() + File_vxlan_vxlan_proto = out.File + file_vxlan_vxlan_proto_rawDesc = nil + file_vxlan_vxlan_proto_goTypes = nil + file_vxlan_vxlan_proto_depIdxs = nil +} diff --git a/vxlan/vxlan.proto b/vxlan/vxlan.proto new file mode 100644 index 0000000..72389b0 --- /dev/null +++ b/vxlan/vxlan.proto @@ -0,0 +1,170 @@ +syntax = "proto3"; +option go_package = "github.com/grmrgecko/virtual-vxlan/vxlan"; + +package vxlan; + +service vxlan { + // Config commands. + rpc SaveConfig (Empty) returns (Empty) {} + rpc ReloadConfig (Empty) returns (Empty) {} + + // Listener commands. + rpc ListListeners (Empty) returns (ListListenersReply) {} + rpc AddListener (Listener) returns (Empty) {} + rpc RemoveListener (ListenerRequestWithName) returns (Empty) {} + rpc SetListenerMaxMessageSize (ListenerMaxMessageSizeRequest) returns (Empty) {} + rpc GetListenerMaxMessageSize (ListenerRequestWithName) returns (ListenerMaxMessageSizeReply) {} + + // Interface commands. + rpc ListInterfaces (ListenerRequestWithName) returns (ListInterfacesReply) {} + rpc AddInterface (AddInterfaceRequest) returns (Empty) {} + rpc RemoveInterface (InterfaceRequestWithName) returns (Empty) {} + rpc SetInterfaceMTU (InterfaceMTURequest) returns (Empty) {} + rpc GetInterfaceMTU (InterfaceRequestWithName) returns (InterfaceMTUReply) {} + rpc SetInterfaceMACAddress (InterfaceMACAddressRequest) returns (Empty) {} + rpc GetInterfaceMACAddress (InterfaceRequestWithName) returns (InterfaceMACAddressReply) {} + rpc SetInterfaceIPAddresses (InterfaceIPAddressesRequest) returns (Empty) {} + rpc GetInterfaceIPAddresses (InterfaceRequestWithName) returns (InterfaceIPAddressesReply) {} + rpc InterfaceAddMACEntry (InterfaceMacEntryRequest) returns (Empty) {} + rpc InterfaceRemoveMACEntry (InterfaceRemoveMacEntryRequest) returns (Empty) {} + rpc InterfaceGetMACEntries (InterfaceRequestWithName) returns (InterfaceMacEntryReply) {} + rpc InterfaceFlushMACTable (InterfaceRequestWithName) returns (Empty) {} + rpc InterfaceAddStaticARPEntry (InterfaceARPEntryRequest) returns (Empty) {} + rpc InterfaceRemoveARPEntry (InterfaceRemoveARPEntryRequest) returns (Empty) {} + rpc InterfaceGetARPEntries (InterfaceRequestWithName) returns (InterfaceArpEntryReply) {} + rpc InterfaceFlushARPTable (InterfaceRequestWithName) returns (Empty) {} +} + +// Standard empty message. +message Empty { +} + +// Listener messages. +message ListenerRequestWithName { + string name = 1; +} + +message Listener { + string name = 1; + string address = 2; + int32 maxMessageSize = 3; + bool permanent = 4; +} + +message ListListenersReply { + repeated Listener listeners = 1; +} + +message ListenerMaxMessageSizeRequest { + string name = 1; + int32 size = 2; +} + +message ListenerMaxMessageSizeReply { + int32 size = 1; +} + +// Interface messages. +message Interface { + string name = 1; + uint32 vni = 2; + int32 mtu = 3; + bool permanent = 4; +} + +message ListInterfacesReply { + repeated Interface interfaces = 1; +} + +message AddInterfaceRequest { + string listenerName = 1; + string name = 2; + uint32 vni = 3; + int32 mtu = 4; + bool permanent = 5; +} + +message InterfaceRequestWithName { + string listenerName = 1; + string name = 2; +} + +message InterfaceMTURequest { + string listenerName = 1; + string name = 2; + int32 mtu = 3; +} + +message InterfaceMTUReply { + int32 mtu = 1; +} + +message InterfaceMACAddressRequest { + string listenerName = 1; + string name = 2; + string mac = 3; +} + +message InterfaceMACAddressReply { + string mac = 1; +} + +message InterfaceIPAddressesRequest { + string listenerName = 1; + string name = 2; + repeated string ipAddress = 3; +} + +message InterfaceIPAddressesReply { + repeated string ipAddress = 1; +} + +message InterfaceMacEntryRequest { + string listenerName = 1; + string name = 2; + string mac = 3; + string destination = 4; + bool permanent = 5; +} + +message InterfaceRemoveMacEntryRequest { + string listenerName = 1; + string name = 2; + string mac = 3; + string destination = 4; +} + +message MacEntry { + string mac = 1; + string destination = 2; + bool permanent = 3; +} + +message InterfaceMacEntryReply { + repeated MacEntry entries = 1; +} + +message InterfaceARPEntryRequest { + string listenerName = 1; + string name = 2; + string address = 3; + string mac = 4; + bool permanent = 5; +} + +message InterfaceRemoveARPEntryRequest { + string listenerName = 1; + string name = 2; + string address = 3; +} + +message ArpEntry { + string address = 1; + string mac = 2; + string expires = 3; + bool permanent = 4; +} + +message InterfaceArpEntryReply { + repeated ArpEntry entries = 1; +} diff --git a/vxlan/vxlan_grpc.pb.go b/vxlan/vxlan_grpc.pb.go new file mode 100644 index 0000000..e883653 --- /dev/null +++ b/vxlan/vxlan_grpc.pb.go @@ -0,0 +1,1001 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.2 +// source: vxlan/vxlan.proto + +package vxlan + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Vxlan_SaveConfig_FullMethodName = "/vxlan.vxlan/SaveConfig" + Vxlan_ReloadConfig_FullMethodName = "/vxlan.vxlan/ReloadConfig" + Vxlan_ListListeners_FullMethodName = "/vxlan.vxlan/ListListeners" + Vxlan_AddListener_FullMethodName = "/vxlan.vxlan/AddListener" + Vxlan_RemoveListener_FullMethodName = "/vxlan.vxlan/RemoveListener" + Vxlan_SetListenerMaxMessageSize_FullMethodName = "/vxlan.vxlan/SetListenerMaxMessageSize" + Vxlan_GetListenerMaxMessageSize_FullMethodName = "/vxlan.vxlan/GetListenerMaxMessageSize" + Vxlan_ListInterfaces_FullMethodName = "/vxlan.vxlan/ListInterfaces" + Vxlan_AddInterface_FullMethodName = "/vxlan.vxlan/AddInterface" + Vxlan_RemoveInterface_FullMethodName = "/vxlan.vxlan/RemoveInterface" + Vxlan_SetInterfaceMTU_FullMethodName = "/vxlan.vxlan/SetInterfaceMTU" + Vxlan_GetInterfaceMTU_FullMethodName = "/vxlan.vxlan/GetInterfaceMTU" + Vxlan_SetInterfaceMACAddress_FullMethodName = "/vxlan.vxlan/SetInterfaceMACAddress" + Vxlan_GetInterfaceMACAddress_FullMethodName = "/vxlan.vxlan/GetInterfaceMACAddress" + Vxlan_SetInterfaceIPAddresses_FullMethodName = "/vxlan.vxlan/SetInterfaceIPAddresses" + Vxlan_GetInterfaceIPAddresses_FullMethodName = "/vxlan.vxlan/GetInterfaceIPAddresses" + Vxlan_InterfaceAddMACEntry_FullMethodName = "/vxlan.vxlan/InterfaceAddMACEntry" + Vxlan_InterfaceRemoveMACEntry_FullMethodName = "/vxlan.vxlan/InterfaceRemoveMACEntry" + Vxlan_InterfaceGetMACEntries_FullMethodName = "/vxlan.vxlan/InterfaceGetMACEntries" + Vxlan_InterfaceFlushMACTable_FullMethodName = "/vxlan.vxlan/InterfaceFlushMACTable" + Vxlan_InterfaceAddStaticARPEntry_FullMethodName = "/vxlan.vxlan/InterfaceAddStaticARPEntry" + Vxlan_InterfaceRemoveARPEntry_FullMethodName = "/vxlan.vxlan/InterfaceRemoveARPEntry" + Vxlan_InterfaceGetARPEntries_FullMethodName = "/vxlan.vxlan/InterfaceGetARPEntries" + Vxlan_InterfaceFlushARPTable_FullMethodName = "/vxlan.vxlan/InterfaceFlushARPTable" +) + +// VxlanClient is the client API for Vxlan service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type VxlanClient interface { + // Config commands. + SaveConfig(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + ReloadConfig(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + // Listener commands. + ListListeners(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListListenersReply, error) + AddListener(ctx context.Context, in *Listener, opts ...grpc.CallOption) (*Empty, error) + RemoveListener(ctx context.Context, in *ListenerRequestWithName, opts ...grpc.CallOption) (*Empty, error) + SetListenerMaxMessageSize(ctx context.Context, in *ListenerMaxMessageSizeRequest, opts ...grpc.CallOption) (*Empty, error) + GetListenerMaxMessageSize(ctx context.Context, in *ListenerRequestWithName, opts ...grpc.CallOption) (*ListenerMaxMessageSizeReply, error) + // Interface commands. + ListInterfaces(ctx context.Context, in *ListenerRequestWithName, opts ...grpc.CallOption) (*ListInterfacesReply, error) + AddInterface(ctx context.Context, in *AddInterfaceRequest, opts ...grpc.CallOption) (*Empty, error) + RemoveInterface(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*Empty, error) + SetInterfaceMTU(ctx context.Context, in *InterfaceMTURequest, opts ...grpc.CallOption) (*Empty, error) + GetInterfaceMTU(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceMTUReply, error) + SetInterfaceMACAddress(ctx context.Context, in *InterfaceMACAddressRequest, opts ...grpc.CallOption) (*Empty, error) + GetInterfaceMACAddress(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceMACAddressReply, error) + SetInterfaceIPAddresses(ctx context.Context, in *InterfaceIPAddressesRequest, opts ...grpc.CallOption) (*Empty, error) + GetInterfaceIPAddresses(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceIPAddressesReply, error) + InterfaceAddMACEntry(ctx context.Context, in *InterfaceMacEntryRequest, opts ...grpc.CallOption) (*Empty, error) + InterfaceRemoveMACEntry(ctx context.Context, in *InterfaceRemoveMacEntryRequest, opts ...grpc.CallOption) (*Empty, error) + InterfaceGetMACEntries(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceMacEntryReply, error) + InterfaceFlushMACTable(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*Empty, error) + InterfaceAddStaticARPEntry(ctx context.Context, in *InterfaceARPEntryRequest, opts ...grpc.CallOption) (*Empty, error) + InterfaceRemoveARPEntry(ctx context.Context, in *InterfaceRemoveARPEntryRequest, opts ...grpc.CallOption) (*Empty, error) + InterfaceGetARPEntries(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceArpEntryReply, error) + InterfaceFlushARPTable(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*Empty, error) +} + +type vxlanClient struct { + cc grpc.ClientConnInterface +} + +func NewVxlanClient(cc grpc.ClientConnInterface) VxlanClient { + return &vxlanClient{cc} +} + +func (c *vxlanClient) SaveConfig(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_SaveConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) ReloadConfig(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_ReloadConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) ListListeners(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListListenersReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListListenersReply) + err := c.cc.Invoke(ctx, Vxlan_ListListeners_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) AddListener(ctx context.Context, in *Listener, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_AddListener_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) RemoveListener(ctx context.Context, in *ListenerRequestWithName, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_RemoveListener_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) SetListenerMaxMessageSize(ctx context.Context, in *ListenerMaxMessageSizeRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_SetListenerMaxMessageSize_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) GetListenerMaxMessageSize(ctx context.Context, in *ListenerRequestWithName, opts ...grpc.CallOption) (*ListenerMaxMessageSizeReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListenerMaxMessageSizeReply) + err := c.cc.Invoke(ctx, Vxlan_GetListenerMaxMessageSize_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) ListInterfaces(ctx context.Context, in *ListenerRequestWithName, opts ...grpc.CallOption) (*ListInterfacesReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListInterfacesReply) + err := c.cc.Invoke(ctx, Vxlan_ListInterfaces_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) AddInterface(ctx context.Context, in *AddInterfaceRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_AddInterface_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) RemoveInterface(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_RemoveInterface_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) SetInterfaceMTU(ctx context.Context, in *InterfaceMTURequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_SetInterfaceMTU_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) GetInterfaceMTU(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceMTUReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InterfaceMTUReply) + err := c.cc.Invoke(ctx, Vxlan_GetInterfaceMTU_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) SetInterfaceMACAddress(ctx context.Context, in *InterfaceMACAddressRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_SetInterfaceMACAddress_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) GetInterfaceMACAddress(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceMACAddressReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InterfaceMACAddressReply) + err := c.cc.Invoke(ctx, Vxlan_GetInterfaceMACAddress_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) SetInterfaceIPAddresses(ctx context.Context, in *InterfaceIPAddressesRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_SetInterfaceIPAddresses_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) GetInterfaceIPAddresses(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceIPAddressesReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InterfaceIPAddressesReply) + err := c.cc.Invoke(ctx, Vxlan_GetInterfaceIPAddresses_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceAddMACEntry(ctx context.Context, in *InterfaceMacEntryRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_InterfaceAddMACEntry_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceRemoveMACEntry(ctx context.Context, in *InterfaceRemoveMacEntryRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_InterfaceRemoveMACEntry_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceGetMACEntries(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceMacEntryReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InterfaceMacEntryReply) + err := c.cc.Invoke(ctx, Vxlan_InterfaceGetMACEntries_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceFlushMACTable(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_InterfaceFlushMACTable_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceAddStaticARPEntry(ctx context.Context, in *InterfaceARPEntryRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_InterfaceAddStaticARPEntry_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceRemoveARPEntry(ctx context.Context, in *InterfaceRemoveARPEntryRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_InterfaceRemoveARPEntry_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceGetARPEntries(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*InterfaceArpEntryReply, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InterfaceArpEntryReply) + err := c.cc.Invoke(ctx, Vxlan_InterfaceGetARPEntries_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vxlanClient) InterfaceFlushARPTable(ctx context.Context, in *InterfaceRequestWithName, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Vxlan_InterfaceFlushARPTable_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// VxlanServer is the server API for Vxlan service. +// All implementations must embed UnimplementedVxlanServer +// for forward compatibility. +type VxlanServer interface { + // Config commands. + SaveConfig(context.Context, *Empty) (*Empty, error) + ReloadConfig(context.Context, *Empty) (*Empty, error) + // Listener commands. + ListListeners(context.Context, *Empty) (*ListListenersReply, error) + AddListener(context.Context, *Listener) (*Empty, error) + RemoveListener(context.Context, *ListenerRequestWithName) (*Empty, error) + SetListenerMaxMessageSize(context.Context, *ListenerMaxMessageSizeRequest) (*Empty, error) + GetListenerMaxMessageSize(context.Context, *ListenerRequestWithName) (*ListenerMaxMessageSizeReply, error) + // Interface commands. + ListInterfaces(context.Context, *ListenerRequestWithName) (*ListInterfacesReply, error) + AddInterface(context.Context, *AddInterfaceRequest) (*Empty, error) + RemoveInterface(context.Context, *InterfaceRequestWithName) (*Empty, error) + SetInterfaceMTU(context.Context, *InterfaceMTURequest) (*Empty, error) + GetInterfaceMTU(context.Context, *InterfaceRequestWithName) (*InterfaceMTUReply, error) + SetInterfaceMACAddress(context.Context, *InterfaceMACAddressRequest) (*Empty, error) + GetInterfaceMACAddress(context.Context, *InterfaceRequestWithName) (*InterfaceMACAddressReply, error) + SetInterfaceIPAddresses(context.Context, *InterfaceIPAddressesRequest) (*Empty, error) + GetInterfaceIPAddresses(context.Context, *InterfaceRequestWithName) (*InterfaceIPAddressesReply, error) + InterfaceAddMACEntry(context.Context, *InterfaceMacEntryRequest) (*Empty, error) + InterfaceRemoveMACEntry(context.Context, *InterfaceRemoveMacEntryRequest) (*Empty, error) + InterfaceGetMACEntries(context.Context, *InterfaceRequestWithName) (*InterfaceMacEntryReply, error) + InterfaceFlushMACTable(context.Context, *InterfaceRequestWithName) (*Empty, error) + InterfaceAddStaticARPEntry(context.Context, *InterfaceARPEntryRequest) (*Empty, error) + InterfaceRemoveARPEntry(context.Context, *InterfaceRemoveARPEntryRequest) (*Empty, error) + InterfaceGetARPEntries(context.Context, *InterfaceRequestWithName) (*InterfaceArpEntryReply, error) + InterfaceFlushARPTable(context.Context, *InterfaceRequestWithName) (*Empty, error) + mustEmbedUnimplementedVxlanServer() +} + +// UnimplementedVxlanServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedVxlanServer struct{} + +func (UnimplementedVxlanServer) SaveConfig(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SaveConfig not implemented") +} +func (UnimplementedVxlanServer) ReloadConfig(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReloadConfig not implemented") +} +func (UnimplementedVxlanServer) ListListeners(context.Context, *Empty) (*ListListenersReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListListeners not implemented") +} +func (UnimplementedVxlanServer) AddListener(context.Context, *Listener) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddListener not implemented") +} +func (UnimplementedVxlanServer) RemoveListener(context.Context, *ListenerRequestWithName) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveListener not implemented") +} +func (UnimplementedVxlanServer) SetListenerMaxMessageSize(context.Context, *ListenerMaxMessageSizeRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetListenerMaxMessageSize not implemented") +} +func (UnimplementedVxlanServer) GetListenerMaxMessageSize(context.Context, *ListenerRequestWithName) (*ListenerMaxMessageSizeReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetListenerMaxMessageSize not implemented") +} +func (UnimplementedVxlanServer) ListInterfaces(context.Context, *ListenerRequestWithName) (*ListInterfacesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListInterfaces not implemented") +} +func (UnimplementedVxlanServer) AddInterface(context.Context, *AddInterfaceRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddInterface not implemented") +} +func (UnimplementedVxlanServer) RemoveInterface(context.Context, *InterfaceRequestWithName) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveInterface not implemented") +} +func (UnimplementedVxlanServer) SetInterfaceMTU(context.Context, *InterfaceMTURequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetInterfaceMTU not implemented") +} +func (UnimplementedVxlanServer) GetInterfaceMTU(context.Context, *InterfaceRequestWithName) (*InterfaceMTUReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInterfaceMTU not implemented") +} +func (UnimplementedVxlanServer) SetInterfaceMACAddress(context.Context, *InterfaceMACAddressRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetInterfaceMACAddress not implemented") +} +func (UnimplementedVxlanServer) GetInterfaceMACAddress(context.Context, *InterfaceRequestWithName) (*InterfaceMACAddressReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInterfaceMACAddress not implemented") +} +func (UnimplementedVxlanServer) SetInterfaceIPAddresses(context.Context, *InterfaceIPAddressesRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetInterfaceIPAddresses not implemented") +} +func (UnimplementedVxlanServer) GetInterfaceIPAddresses(context.Context, *InterfaceRequestWithName) (*InterfaceIPAddressesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInterfaceIPAddresses not implemented") +} +func (UnimplementedVxlanServer) InterfaceAddMACEntry(context.Context, *InterfaceMacEntryRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceAddMACEntry not implemented") +} +func (UnimplementedVxlanServer) InterfaceRemoveMACEntry(context.Context, *InterfaceRemoveMacEntryRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceRemoveMACEntry not implemented") +} +func (UnimplementedVxlanServer) InterfaceGetMACEntries(context.Context, *InterfaceRequestWithName) (*InterfaceMacEntryReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceGetMACEntries not implemented") +} +func (UnimplementedVxlanServer) InterfaceFlushMACTable(context.Context, *InterfaceRequestWithName) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceFlushMACTable not implemented") +} +func (UnimplementedVxlanServer) InterfaceAddStaticARPEntry(context.Context, *InterfaceARPEntryRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceAddStaticARPEntry not implemented") +} +func (UnimplementedVxlanServer) InterfaceRemoveARPEntry(context.Context, *InterfaceRemoveARPEntryRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceRemoveARPEntry not implemented") +} +func (UnimplementedVxlanServer) InterfaceGetARPEntries(context.Context, *InterfaceRequestWithName) (*InterfaceArpEntryReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceGetARPEntries not implemented") +} +func (UnimplementedVxlanServer) InterfaceFlushARPTable(context.Context, *InterfaceRequestWithName) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InterfaceFlushARPTable not implemented") +} +func (UnimplementedVxlanServer) mustEmbedUnimplementedVxlanServer() {} +func (UnimplementedVxlanServer) testEmbeddedByValue() {} + +// UnsafeVxlanServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to VxlanServer will +// result in compilation errors. +type UnsafeVxlanServer interface { + mustEmbedUnimplementedVxlanServer() +} + +func RegisterVxlanServer(s grpc.ServiceRegistrar, srv VxlanServer) { + // If the following call pancis, it indicates UnimplementedVxlanServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Vxlan_ServiceDesc, srv) +} + +func _Vxlan_SaveConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).SaveConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_SaveConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).SaveConfig(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_ReloadConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).ReloadConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_ReloadConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).ReloadConfig(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_ListListeners_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).ListListeners(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_ListListeners_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).ListListeners(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_AddListener_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Listener) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).AddListener(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_AddListener_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).AddListener(ctx, req.(*Listener)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_RemoveListener_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListenerRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).RemoveListener(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_RemoveListener_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).RemoveListener(ctx, req.(*ListenerRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_SetListenerMaxMessageSize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListenerMaxMessageSizeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).SetListenerMaxMessageSize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_SetListenerMaxMessageSize_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).SetListenerMaxMessageSize(ctx, req.(*ListenerMaxMessageSizeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_GetListenerMaxMessageSize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListenerRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).GetListenerMaxMessageSize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_GetListenerMaxMessageSize_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).GetListenerMaxMessageSize(ctx, req.(*ListenerRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_ListInterfaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListenerRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).ListInterfaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_ListInterfaces_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).ListInterfaces(ctx, req.(*ListenerRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_AddInterface_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddInterfaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).AddInterface(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_AddInterface_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).AddInterface(ctx, req.(*AddInterfaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_RemoveInterface_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).RemoveInterface(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_RemoveInterface_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).RemoveInterface(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_SetInterfaceMTU_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceMTURequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).SetInterfaceMTU(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_SetInterfaceMTU_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).SetInterfaceMTU(ctx, req.(*InterfaceMTURequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_GetInterfaceMTU_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).GetInterfaceMTU(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_GetInterfaceMTU_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).GetInterfaceMTU(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_SetInterfaceMACAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceMACAddressRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).SetInterfaceMACAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_SetInterfaceMACAddress_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).SetInterfaceMACAddress(ctx, req.(*InterfaceMACAddressRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_GetInterfaceMACAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).GetInterfaceMACAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_GetInterfaceMACAddress_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).GetInterfaceMACAddress(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_SetInterfaceIPAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceIPAddressesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).SetInterfaceIPAddresses(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_SetInterfaceIPAddresses_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).SetInterfaceIPAddresses(ctx, req.(*InterfaceIPAddressesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_GetInterfaceIPAddresses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).GetInterfaceIPAddresses(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_GetInterfaceIPAddresses_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).GetInterfaceIPAddresses(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceAddMACEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceMacEntryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceAddMACEntry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceAddMACEntry_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceAddMACEntry(ctx, req.(*InterfaceMacEntryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceRemoveMACEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRemoveMacEntryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceRemoveMACEntry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceRemoveMACEntry_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceRemoveMACEntry(ctx, req.(*InterfaceRemoveMacEntryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceGetMACEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceGetMACEntries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceGetMACEntries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceGetMACEntries(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceFlushMACTable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceFlushMACTable(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceFlushMACTable_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceFlushMACTable(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceAddStaticARPEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceARPEntryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceAddStaticARPEntry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceAddStaticARPEntry_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceAddStaticARPEntry(ctx, req.(*InterfaceARPEntryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceRemoveARPEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRemoveARPEntryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceRemoveARPEntry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceRemoveARPEntry_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceRemoveARPEntry(ctx, req.(*InterfaceRemoveARPEntryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceGetARPEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceGetARPEntries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceGetARPEntries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceGetARPEntries(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vxlan_InterfaceFlushARPTable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InterfaceRequestWithName) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VxlanServer).InterfaceFlushARPTable(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Vxlan_InterfaceFlushARPTable_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VxlanServer).InterfaceFlushARPTable(ctx, req.(*InterfaceRequestWithName)) + } + return interceptor(ctx, in, info, handler) +} + +// Vxlan_ServiceDesc is the grpc.ServiceDesc for Vxlan service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Vxlan_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "vxlan.vxlan", + HandlerType: (*VxlanServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SaveConfig", + Handler: _Vxlan_SaveConfig_Handler, + }, + { + MethodName: "ReloadConfig", + Handler: _Vxlan_ReloadConfig_Handler, + }, + { + MethodName: "ListListeners", + Handler: _Vxlan_ListListeners_Handler, + }, + { + MethodName: "AddListener", + Handler: _Vxlan_AddListener_Handler, + }, + { + MethodName: "RemoveListener", + Handler: _Vxlan_RemoveListener_Handler, + }, + { + MethodName: "SetListenerMaxMessageSize", + Handler: _Vxlan_SetListenerMaxMessageSize_Handler, + }, + { + MethodName: "GetListenerMaxMessageSize", + Handler: _Vxlan_GetListenerMaxMessageSize_Handler, + }, + { + MethodName: "ListInterfaces", + Handler: _Vxlan_ListInterfaces_Handler, + }, + { + MethodName: "AddInterface", + Handler: _Vxlan_AddInterface_Handler, + }, + { + MethodName: "RemoveInterface", + Handler: _Vxlan_RemoveInterface_Handler, + }, + { + MethodName: "SetInterfaceMTU", + Handler: _Vxlan_SetInterfaceMTU_Handler, + }, + { + MethodName: "GetInterfaceMTU", + Handler: _Vxlan_GetInterfaceMTU_Handler, + }, + { + MethodName: "SetInterfaceMACAddress", + Handler: _Vxlan_SetInterfaceMACAddress_Handler, + }, + { + MethodName: "GetInterfaceMACAddress", + Handler: _Vxlan_GetInterfaceMACAddress_Handler, + }, + { + MethodName: "SetInterfaceIPAddresses", + Handler: _Vxlan_SetInterfaceIPAddresses_Handler, + }, + { + MethodName: "GetInterfaceIPAddresses", + Handler: _Vxlan_GetInterfaceIPAddresses_Handler, + }, + { + MethodName: "InterfaceAddMACEntry", + Handler: _Vxlan_InterfaceAddMACEntry_Handler, + }, + { + MethodName: "InterfaceRemoveMACEntry", + Handler: _Vxlan_InterfaceRemoveMACEntry_Handler, + }, + { + MethodName: "InterfaceGetMACEntries", + Handler: _Vxlan_InterfaceGetMACEntries_Handler, + }, + { + MethodName: "InterfaceFlushMACTable", + Handler: _Vxlan_InterfaceFlushMACTable_Handler, + }, + { + MethodName: "InterfaceAddStaticARPEntry", + Handler: _Vxlan_InterfaceAddStaticARPEntry_Handler, + }, + { + MethodName: "InterfaceRemoveARPEntry", + Handler: _Vxlan_InterfaceRemoveARPEntry_Handler, + }, + { + MethodName: "InterfaceGetARPEntries", + Handler: _Vxlan_InterfaceGetARPEntries_Handler, + }, + { + MethodName: "InterfaceFlushARPTable", + Handler: _Vxlan_InterfaceFlushARPTable_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "vxlan/vxlan.proto", +} diff --git a/wintun-source.md b/wintun-source.md new file mode 100644 index 0000000..a346351 --- /dev/null +++ b/wintun-source.md @@ -0,0 +1,7 @@ +# Wintun + +This driver was version [0.14.1](https://www.wintun.net/builds/wintun-0.14.1.zip) downloaded from [wintun.net](https://www.wintun.net/). The source code is provided under the GPL 2.0 and [is available via git](https://git.zx2c4.com/wintun): + +``` +git clone https://git.zx2c4.com/wintun +``` diff --git a/wintun/LICENSE.txt b/wintun/LICENSE.txt new file mode 100644 index 0000000..bc8d3f0 --- /dev/null +++ b/wintun/LICENSE.txt @@ -0,0 +1,84 @@ +Prebuilt Binaries License +------------------------- + +1. DEFINITIONS. "Software" means the precise contents of the "wintun.dll" + files that are included in the .zip file that contains this document as + downloaded from wintun.net/builds. + +2. LICENSE GRANT. WireGuard LLC grants to you a non-exclusive and + non-transferable right to use Software for lawful purposes under certain + obligations and limited rights as set forth in this agreement. + +3. RESTRICTIONS. Software is owned and copyrighted by WireGuard LLC. It is + licensed, not sold. Title to Software and all associated intellectual + property rights are retained by WireGuard. You must not: + a. reverse engineer, decompile, disassemble, extract from, or otherwise + modify the Software; + b. modify or create derivative work based upon Software in whole or in + parts, except insofar as only the API interfaces of the "wintun.h" file + distributed alongside the Software (the "Permitted API") are used; + c. remove any proprietary notices, labels, or copyrights from the Software; + d. resell, redistribute, lease, rent, transfer, sublicense, or otherwise + transfer rights of the Software without the prior written consent of + WireGuard LLC, except insofar as the Software is distributed alongside + other software that uses the Software only via the Permitted API; + e. use the name of WireGuard LLC, the WireGuard project, the Wintun + project, or the names of its contributors to endorse or promote products + derived from the Software without specific prior written consent. + +4. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF + ANY KIND. WIREGUARD LLC HEREBY EXCLUDES AND DISCLAIMS ALL IMPLIED OR + STATUTORY WARRANTIES, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE, QUALITY, NON-INFRINGEMENT, TITLE, RESULTS, + EFFORTS, OR QUIET ENJOYMENT. THERE IS NO WARRANTY THAT THE PRODUCT WILL BE + ERROR-FREE OR WILL FUNCTION WITHOUT INTERRUPTION. YOU ASSUME THE ENTIRE + RISK FOR THE RESULTS OBTAINED USING THE PRODUCT. TO THE EXTENT THAT + WIREGUARD LLC MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW, + THE SCOPE AND DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER + SUCH LAW. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND + WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR + A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE + EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. + +5. LIMITATION OF LIABILITY. To the extent not prohibited by law, in no event + WireGuard LLC or any third-party-developer will be liable for any lost + revenue, profit or data or for special, indirect, consequential, incidental + or punitive damages, however caused regardless of the theory of liability, + arising out of or related to the use of or inability to use Software, even + if WireGuard LLC has been advised of the possibility of such damages. + Solely you are responsible for determining the appropriateness of using + Software and accept full responsibility for all risks associated with its + exercise of rights under this agreement, including but not limited to the + risks and costs of program errors, compliance with applicable laws, damage + to or loss of data, programs or equipment, and unavailability or + interruption of operations. The foregoing limitations will apply even if + the above stated warranty fails of its essential purpose. You acknowledge, + that it is in the nature of software that software is complex and not + completely free of errors. In no event shall WireGuard LLC or any + third-party-developer be liable to you under any theory for any damages + suffered by you or any user of Software or for any special, incidental, + indirect, consequential or similar damages (including without limitation + damages for loss of business profits, business interruption, loss of + business information or any other pecuniary loss) arising out of the use or + inability to use Software, even if WireGuard LLC has been advised of the + possibility of such damages and regardless of the legal or quitable theory + (contract, tort, or otherwise) upon which the claim is based. + +6. TERMINATION. This agreement is affected until terminated. You may + terminate this agreement at any time. This agreement will terminate + immediately without notice from WireGuard LLC if you fail to comply with + the terms and conditions of this agreement. Upon termination, you must + delete Software and all copies of Software and cease all forms of + distribution of Software. + +7. SEVERABILITY. If any provision of this agreement is held to be + unenforceable, this agreement will remain in effect with the provision + omitted, unless omission would frustrate the intent of the parties, in + which case this agreement will immediately terminate. + +8. RESERVATION OF RIGHTS. All rights not expressly granted in this agreement + are reserved by WireGuard LLC. For example, WireGuard LLC reserves the + right at any time to cease development of Software, to alter distribution + details, features, specifications, capabilities, functions, licensing + terms, release dates, APIs, ABIs, general availability, or other + characteristics of the Software. diff --git a/wintun/README.md b/wintun/README.md new file mode 100644 index 0000000..16e6a68 --- /dev/null +++ b/wintun/README.md @@ -0,0 +1,339 @@ +# [Wintun Network Adapter](https://www.wintun.net/) +### TUN Device Driver for Windows + +This is a layer 3 TUN driver for Windows 7, 8, 8.1, and 10. Originally created for [WireGuard](https://www.wireguard.com/), it is intended to be useful to a wide variety of projects that require layer 3 tunneling devices with implementations primarily in userspace. + +## Installation + +Wintun is deployed as a platform-specific `wintun.dll` file. Install the `wintun.dll` file side-by-side with your application. Download the dll from [wintun.net](https://www.wintun.net/), alongside the header file for your application described below. + +## Usage + +Include the [`wintun.h` file](https://git.zx2c4.com/wintun/tree/api/wintun.h) in your project simply by copying it there and dynamically load the `wintun.dll` using [`LoadLibraryEx()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa) and [`GetProcAddress()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress) to resolve each function, using the typedefs provided in the header file. The [`InitializeWintun` function in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c) provides this in a function that you can simply copy and paste. + +With the library setup, Wintun can then be used by first creating an adapter, configuring it, and then setting its status to "up". Adapters have names (e.g. "OfficeNet") and types (e.g. "Wintun"). + +```C +WINTUN_ADAPTER_HANDLE Adapter1 = WintunCreateAdapter(L"OfficeNet", L"Wintun", &SomeFixedGUID1); +WINTUN_ADAPTER_HANDLE Adapter2 = WintunCreateAdapter(L"HomeNet", L"Wintun", &SomeFixedGUID2); +WINTUN_ADAPTER_HANDLE Adapter3 = WintunCreateAdapter(L"Data Center", L"Wintun", &SomeFixedGUID3); +``` + +After creating an adapter, we can use it by starting a session: + +```C +WINTUN_SESSION_HANDLE Session = WintunStartSession(Adapter2, 0x400000); +``` + +Then, the `WintunAllocateSendPacket` and `WintunSendPacket` functions can be used for sending packets ([used by `SendPackets` in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c)): + +```C +BYTE *OutgoingPacket = WintunAllocateSendPacket(Session, PacketDataSize); +if (OutgoingPacket) +{ + memcpy(OutgoingPacket, PacketData, PacketDataSize); + WintunSendPacket(Session, OutgoingPacket); +} +else if (GetLastError() != ERROR_BUFFER_OVERFLOW) // Silently drop packets if the ring is full + Log(L"Packet write failed"); +``` + +And the `WintunReceivePacket` and `WintunReleaseReceivePacket` functions can be used for receiving packets ([used by `ReceivePackets` in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c)): + +```C +for (;;) +{ + DWORD IncomingPacketSize; + BYTE *IncomingPacket = WintunReceivePacket(Session, &IncomingPacketSize); + if (IncomingPacket) + { + DoSomethingWithPacket(IncomingPacket, IncomingPacketSize); + WintunReleaseReceivePacket(Session, IncomingPacket); + } + else if (GetLastError() == ERROR_NO_MORE_ITEMS) + WaitForSingleObject(WintunGetReadWaitEvent(Session), INFINITE); + else + { + Log(L"Packet read failed"); + break; + } +} +``` + +Some high performance use cases may want to spin on `WintunReceivePackets` for a number of cycles before falling back to waiting on the read-wait event. + +You are **highly encouraged** to read the [**example.c short example**](https://git.zx2c4.com/wintun/tree/example/example.c) to see how to put together a simple userspace network tunnel. + +The various functions and definitions are [documented in the reference below](#Reference). + +## Reference + +### Macro Definitions + +#### WINTUN\_MAX\_POOL + +`#define WINTUN_MAX_POOL 256` + +Maximum pool name length including zero terminator + +#### WINTUN\_MIN\_RING\_CAPACITY + +`#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */` + +Minimum ring capacity. + +#### WINTUN\_MAX\_RING\_CAPACITY + +`#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */` + +Maximum ring capacity. + +#### WINTUN\_MAX\_IP\_PACKET\_SIZE + +`#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF` + +Maximum IP packet size + +### Typedefs + +#### WINTUN\_ADAPTER\_HANDLE + +`typedef void* WINTUN_ADAPTER_HANDLE` + +A handle representing Wintun adapter + +#### WINTUN\_ENUM\_CALLBACK + +`typedef BOOL(* WINTUN_ENUM_CALLBACK) (WINTUN_ADAPTER_HANDLE Adapter, LPARAM Param)` + +Called by WintunEnumAdapters for each adapter in the pool. + +**Parameters** + +- *Adapter*: Adapter handle, which will be freed when this function returns. +- *Param*: An application-defined value passed to the WintunEnumAdapters. + +**Returns** + +Non-zero to continue iterating adapters; zero to stop. + +#### WINTUN\_LOGGER\_CALLBACK + +`typedef void(* WINTUN_LOGGER_CALLBACK) (WINTUN_LOGGER_LEVEL Level, DWORD64 Timestamp, const WCHAR *Message)` + +Called by internal logger to report diagnostic messages + +**Parameters** + +- *Level*: Message level. +- *Timestamp*: Message timestamp in in 100ns intervals since 1601-01-01 UTC. +- *Message*: Message text. + +#### WINTUN\_SESSION\_HANDLE + +`typedef void* WINTUN_SESSION_HANDLE` + +A handle representing Wintun session + +### Enumeration Types + +#### WINTUN\_LOGGER\_LEVEL + +`enum WINTUN_LOGGER_LEVEL` + +Determines the level of logging, passed to WINTUN\_LOGGER\_CALLBACK. + +- *WINTUN\_LOG\_INFO*: Informational +- *WINTUN\_LOG\_WARN*: Warning +- *WINTUN\_LOG\_ERR*: Error + +Enumerator + +### Functions + +#### WintunCreateAdapter() + +`WINTUN_ADAPTER_HANDLE WintunCreateAdapter (const WCHAR * Name, const WCHAR * TunnelType, const GUID * RequestedGUID)` + +Creates a new Wintun adapter. + +**Parameters** + +- *Name*: The requested name of the adapter. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. +- *Name*: Name of the adapter tunnel type. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. +- *RequestedGUID*: The GUID of the created network adapter, which then influences NLA generation deterministically. If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is created for each new adapter. It is called "requested" GUID because the API it uses is completely undocumented, and so there could be minor interesting complications with its usage. + +**Returns** + +If the function succeeds, the return value is the adapter handle. Must be released with WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError. + +#### WintunOpenAdapter() + +`WINTUN_ADAPTER_HANDLE WintunOpenAdapter (const WCHAR * Name)` + +Opens an existing Wintun adapter. + +**Parameters** + +- *Name*: The requested name of the adapter. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. + +**Returns** + +If the function succeeds, the return value is adapter handle. Must be released with WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError. + +#### WintunCloseAdapter() + +`void WintunCloseAdapter (WINTUN_ADAPTER_HANDLE Adapter)` + +Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. + +**Parameters** + +- *Adapter*: Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. + +#### WintunDeleteDriver() + +`BOOL WintunDeleteDriver ()` + +Deletes the Wintun driver if there are no more adapters in use. + +**Returns** + +If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. + +#### WintunGetAdapterLuid() + +`void WintunGetAdapterLuid (WINTUN_ADAPTER_HANDLE Adapter, NET_LUID * Luid)` + +Returns the LUID of the adapter. + +**Parameters** + +- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter +- *Luid*: Pointer to LUID to receive adapter LUID. + +#### WintunGetRunningDriverVersion() + +`DWORD WintunGetRunningDriverVersion (void )` + +Determines the version of the Wintun driver currently loaded. + +**Returns** + +If the function succeeds, the return value is the version number. If the function fails, the return value is zero. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_FILE\_NOT\_FOUND Wintun not loaded + +#### WintunSetLogger() + +`void WintunSetLogger (WINTUN_LOGGER_CALLBACK NewLogger)` + +Sets logger callback function. + +**Parameters** + +- *NewLogger*: Pointer to callback function to use as a new global logger. NewLogger may be called from various threads concurrently. Should the logging require serialization, you must handle serialization in NewLogger. Set to NULL to disable. + +#### WintunStartSession() + +`WINTUN_SESSION_HANDLE WintunStartSession (WINTUN_ADAPTER_HANDLE Adapter, DWORD Capacity)` + +Starts Wintun session. + +**Parameters** + +- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter +- *Capacity*: Rings capacity. Must be between WINTUN\_MIN\_RING\_CAPACITY and WINTUN\_MAX\_RING\_CAPACITY (incl.) Must be a power of two. + +**Returns** + +Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is NULL. To get extended error information, call GetLastError. + +#### WintunEndSession() + +`void WintunEndSession (WINTUN_SESSION_HANDLE Session)` + +Ends Wintun session. + +**Parameters** + +- *Session*: Wintun session handle obtained with WintunStartSession + +#### WintunGetReadWaitEvent() + +`HANDLE WintunGetReadWaitEvent (WINTUN_SESSION_HANDLE Session)` + +Gets Wintun session's read-wait event handle. + +**Parameters** + +- *Session*: Wintun session handle obtained with WintunStartSession + +**Returns** + +Pointer to receive event handle to wait for available data when reading. Should WintunReceivePackets return ERROR\_NO\_MORE\_ITEMS (after spinning on it for a while under heavy load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call CloseHandle on this event - it is managed by the session. + +#### WintunReceivePacket() + +`BYTE* WintunReceivePacket (WINTUN_SESSION_HANDLE Session, DWORD * PacketSize)` + +Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned from this function to release internal buffer. This function is thread-safe. + +**Parameters** + +- *Session*: Wintun session handle obtained with WintunStartSession +- *PacketSize*: Pointer to receive packet size. + +**Returns** + +Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_HANDLE\_EOF Wintun adapter is terminating; ERROR\_NO\_MORE\_ITEMS Wintun buffer is exhausted; ERROR\_INVALID\_DATA Wintun buffer is corrupt + +#### WintunReleaseReceivePacket() + +`void WintunReleaseReceivePacket (WINTUN_SESSION_HANDLE Session, const BYTE * Packet)` + +Releases internal buffer after the received packet has been processed by the client. This function is thread-safe. + +**Parameters** + +- *Session*: Wintun session handle obtained with WintunStartSession +- *Packet*: Packet obtained with WintunReceivePacket + +#### WintunAllocateSendPacket() + +`BYTE* WintunAllocateSendPacket (WINTUN_SESSION_HANDLE Session, DWORD PacketSize)` + +Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of calls define the packet sending order. + +**Parameters** + +- *Session*: Wintun session handle obtained with WintunStartSession +- *PacketSize*: Exact packet size. Must be less or equal to WINTUN\_MAX\_IP\_PACKET\_SIZE. + +**Returns** + +Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_HANDLE\_EOF Wintun adapter is terminating; ERROR\_BUFFER\_OVERFLOW Wintun buffer is full; + +#### WintunSendPacket() + +`void WintunSendPacket (WINTUN_SESSION_HANDLE Session, const BYTE * Packet)` + +Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the WintunSendPacket yet. + +**Parameters** + +- *Session*: Wintun session handle obtained with WintunStartSession +- *Packet*: Packet obtained with WintunAllocateSendPacket + +## Building + +**Do not distribute drivers or files named "Wintun", as they will most certainly clash with official deployments. Instead distribute [`wintun.dll` as downloaded from wintun.net](https://www.wintun.net).** + +General requirements: + +- [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) with Windows SDK +- [Windows Driver Kit](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) + +`wintun.sln` may be opened in Visual Studio for development and building. Be sure to run `bcdedit /set testsigning on` and then reboot before to enable unsigned driver loading. The default run sequence (F5) in Visual Studio will build the example project and its dependencies. + +## License + +The entire contents of [the repository](https://git.zx2c4.com/wintun/), including all documentation and example code, is "Copyright © 2018-2021 WireGuard LLC. All Rights Reserved." Source code is licensed under the [GPLv2](COPYING). Prebuilt binaries from [wintun.net](https://www.wintun.net/) are released under a more permissive license suitable for more forms of software contained inside of the .zip files distributed there. diff --git a/wintun/bin/amd64/wintun.dll b/wintun/bin/amd64/wintun.dll new file mode 100644 index 0000000..aee04e7 Binary files /dev/null and b/wintun/bin/amd64/wintun.dll differ diff --git a/wintun/bin/arm/wintun.dll b/wintun/bin/arm/wintun.dll new file mode 100644 index 0000000..0017794 Binary files /dev/null and b/wintun/bin/arm/wintun.dll differ diff --git a/wintun/bin/arm64/wintun.dll b/wintun/bin/arm64/wintun.dll new file mode 100644 index 0000000..dc4e4ae Binary files /dev/null and b/wintun/bin/arm64/wintun.dll differ diff --git a/wintun/bin/x86/wintun.dll b/wintun/bin/x86/wintun.dll new file mode 100644 index 0000000..2ab97db Binary files /dev/null and b/wintun/bin/x86/wintun.dll differ diff --git a/wintun/include/wintun.h b/wintun/include/wintun.h new file mode 100644 index 0000000..55d441f --- /dev/null +++ b/wintun/include/wintun.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ALIGNED +# if defined(_MSC_VER) +# define ALIGNED(n) __declspec(align(n)) +# elif defined(__GNUC__) +# define ALIGNED(n) __attribute__((aligned(n))) +# else +# error "Unable to define ALIGNED" +# endif +#endif + +/* MinGW is missing this one, unfortunately. */ +#ifndef _Post_maybenull_ +# define _Post_maybenull_ +#endif + +#pragma warning(push) +#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */ + +/** + * A handle representing Wintun adapter + */ +typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE; + +/** + * Creates a new Wintun adapter. + * + * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * + * @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * + * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically. + * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is + * created for each new adapter. It is called "requested" GUID because the API it uses is + * completely undocumented, and so there could be minor interesting complications with its usage. + * + * @return If the function succeeds, the return value is the adapter handle. Must be released with + * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call + * GetLastError. + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC) +(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID); + +/** + * Opens an existing Wintun adapter. + * + * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * + * @return If the function succeeds, the return value is the adapter handle. Must be released with + * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call + * GetLastError. + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name); + +/** + * Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. + * + * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. + */ +typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter); + +/** + * Deletes the Wintun driver if there are no more adapters in use. + * + * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To + * get extended error information, call GetLastError. + */ +typedef _Return_type_success_(return != FALSE) +BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID); + +/** + * Returns the LUID of the adapter. + * + * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter + * + * @param Luid Pointer to LUID to receive adapter LUID. + */ +typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid); + +/** + * Determines the version of the Wintun driver currently loaded. + * + * @return If the function succeeds, the return value is the version number. If the function fails, the return value is + * zero. To get extended error information, call GetLastError. Possible errors include the following: + * ERROR_FILE_NOT_FOUND Wintun not loaded + */ +typedef _Return_type_success_(return != 0) +DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID); + +/** + * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK. + */ +typedef enum +{ + WINTUN_LOG_INFO, /**< Informational */ + WINTUN_LOG_WARN, /**< Warning */ + WINTUN_LOG_ERR /**< Error */ +} WINTUN_LOGGER_LEVEL; + +/** + * Called by internal logger to report diagnostic messages + * + * @param Level Message level. + * + * @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC. + * + * @param Message Message text. + */ +typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)( + _In_ WINTUN_LOGGER_LEVEL Level, + _In_ DWORD64 Timestamp, + _In_z_ LPCWSTR Message); + +/** + * Sets logger callback function. + * + * @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various + * threads concurrently. Should the logging require serialization, you must handle serialization in + * NewLogger. Set to NULL to disable. + */ +typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger); + +/** + * Minimum ring capacity. + */ +#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */ + +/** + * Maximum ring capacity. + */ +#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */ + +/** + * A handle representing Wintun session + */ +typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE; + +/** + * Starts Wintun session. + * + * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter + * + * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.) + * Must be a power of two. + * + * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is + * NULL. To get extended error information, call GetLastError. + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity); + +/** + * Ends Wintun session. + * + * @param Session Wintun session handle obtained with WintunStartSession + */ +typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); + +/** + * Gets Wintun session's read-wait event handle. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @return Pointer to receive event handle to wait for available data when reading. Should + * WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy + * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call + * CloseHandle on this event - it is managed by the session. + */ +typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); + +/** + * Maximum IP packet size + */ +#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF + +/** + * Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned + * from this function to release internal buffer. This function is thread-safe. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param PacketSize Pointer to receive packet size. + * + * @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the + * return value is NULL. To get extended error information, call GetLastError. Possible errors include the + * following: + * ERROR_HANDLE_EOF Wintun adapter is terminating; + * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted; + * ERROR_INVALID_DATA Wintun buffer is corrupt + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +_Post_writable_byte_size_(*PacketSize) +BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize); + +/** + * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param Packet Packet obtained with WintunReceivePacket + */ +typedef VOID( + WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); + +/** + * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send + * and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of + * calls define the packet sending order. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE. + * + * @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, + * the return value is NULL. To get extended error information, call GetLastError. Possible errors include the + * following: + * ERROR_HANDLE_EOF Wintun adapter is terminating; + * ERROR_BUFFER_OVERFLOW Wintun buffer is full; + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +_Post_writable_byte_size_(PacketSize) +BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize); + +/** + * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket + * order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the + * WintunSendPacket yet. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param Packet Packet obtained with WintunAllocateSendPacket + */ +typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); + +#pragma warning(pop) + +#ifdef __cplusplus +} +#endif