vip-manager-0.6/000077500000000000000000000000001353774041100135725ustar00rootroot00000000000000vip-manager-0.6/.gitignore000066400000000000000000000000361353774041100155610ustar00rootroot00000000000000/vip-manager *.deb *.rpm tmp/ vip-manager-0.6/Dockerfile000066400000000000000000000004471353774041100155710ustar00rootroot00000000000000FROM golang:1.9.4-alpine3.7 AS build ENV GOPATH /app WORKDIR /app/src/github.com/cybertec-postgresql/vip-manager COPY . . RUN go install FROM alpine:latest RUN apk add --no-cache iproute2 dumb-init COPY --from=build /app/bin/vip-manager / ENTRYPOINT ["/usr/bin/dumb-init", "--", "/vip-manager"] vip-manager-0.6/Gopkg.lock000066400000000000000000000104401353774041100155120ustar00rootroot00000000000000# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] digest = "1:c24b0a8a58ed35844fcff6d3a816bb529492cf9a2fefc80e275ad40de7ad1919" name = "github.com/coreos/etcd" packages = [ "client", "pkg/pathutil", "pkg/srv", "pkg/types", "version", ] pruneopts = "" revision = "98d308426819d892e149fe45f6fd542464cb1f9d" version = "v3.3.13" [[projects]] digest = "1:3c3f68ebab415344aef64363d23471e953a4715645115604aaf57923ae904f5e" name = "github.com/coreos/go-semver" packages = ["semver"] pruneopts = "" revision = "8ab6407b697782a06568d4b7f1db25550ec2e4c6" version = "v0.2.0" [[projects]] digest = "1:3f152ddf1c7cae3fcb0d6a81c4f2e2b16a532bad08a5759320635c0bc1ee77b1" name = "github.com/hashicorp/consul" packages = ["api"] pruneopts = "" revision = "75ca2cace08e38de8af1731ee8614d0533d5a4d4" version = "v0.9.2" [[projects]] branch = "master" digest = "1:ae263e6b149fb7aadfc0f2284a8891cc3b9d2700e58b9a613fecf70fcc6e495d" name = "github.com/hashicorp/go-cleanhttp" packages = ["."] pruneopts = "" revision = "3573b8b52aa7b37b9358d966a898feb387f62437" [[projects]] branch = "master" digest = "1:ff65bf6fc4d1116f94ac305342725c21b55c16819c2606adc8f527755716937f" name = "github.com/hashicorp/go-rootcerts" packages = ["."] pruneopts = "" revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" [[projects]] digest = "1:f72168ea995f398bab88e84bd1ff58a983466ba162fb8d50d47420666cd57fad" name = "github.com/hashicorp/serf" packages = ["coordinate"] pruneopts = "" revision = "d6574a5bb1226678d7010325fb6c985db20ee458" version = "v0.8.1" [[projects]] digest = "1:12d3de2c11e54ea37d7f00daf85088ad5e61ec4e8a1f828d6c8b657976856be7" name = "github.com/json-iterator/go" packages = ["."] pruneopts = "" revision = "0ff49de124c6f76f8494e194af75bde0f1a49a29" version = "v1.1.6" [[projects]] branch = "master" digest = "1:39509fc0baedf78b5d384abe8d01824ac2f58a14760bb57b8a962185deb0eb7b" name = "github.com/mdlayher/arp" packages = ["."] pruneopts = "" revision = "43953fc6354aa71b6d6814755eceba262eae9246" [[projects]] branch = "master" digest = "1:fdbcf8c617ed9a08bc82c19c577664e50698e402af7669a75224858442ba70a6" name = "github.com/mdlayher/ethernet" packages = ["."] pruneopts = "" revision = "e72cf8343052938936194daba1914fd1bb33d2fe" [[projects]] branch = "master" digest = "1:c0e8e4bb5e4b9db286aaa5e98d883a4d7e40a962091eff7549b74ee4a7230c71" name = "github.com/mdlayher/raw" packages = ["."] pruneopts = "" revision = "156e9b63ae185a43fd1726bbee88eb0109afe5f4" [[projects]] branch = "master" digest = "1:59d11e81d6fdd12a771321696bb22abdd9a94d26ac864787e98c9b419e428734" name = "github.com/mitchellh/go-homedir" packages = ["."] pruneopts = "" revision = "b8bc1bf767474819792c23f32d8286a45736f1c6" [[projects]] digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd" name = "github.com/modern-go/concurrent" packages = ["."] pruneopts = "" revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" version = "1.0.3" [[projects]] digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" name = "github.com/modern-go/reflect2" packages = ["."] pruneopts = "" revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" version = "1.0.1" [[projects]] branch = "master" digest = "1:35171304d8332a0cfac5f3bd9222467f036732ddde75c65278b16f65216e03ed" name = "golang.org/x/net" packages = [ "bpf", "context", ] pruneopts = "" revision = "1c05540f6879653db88113bc4a2b70aec4bd491f" [[projects]] branch = "master" digest = "1:a67e20a897efd79402efc713c2c37af983af83ee6a80bdca40d602c12a347283" name = "golang.org/x/sys" packages = ["unix"] pruneopts = "" revision = "92ac112afc6efd90284acda2b046fc0e351228f6" [[projects]] digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d" name = "gopkg.in/yaml.v2" packages = ["."] pruneopts = "" revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" version = "v2.2.2" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/coreos/etcd/client", "github.com/hashicorp/consul/api", "github.com/mdlayher/arp", "gopkg.in/yaml.v2", ] solver-name = "gps-cdcl" solver-version = 1 vip-manager-0.6/Gopkg.toml000066400000000000000000000012351353774041100155370ustar00rootroot00000000000000 # Gopkg.toml example # # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md # for detailed Gopkg.toml documentation. # # required = ["github.com/user/thing/cmd/thing"] # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] # # [[constraint]] # name = "github.com/user/project" # version = "1.0.0" # # [[constraint]] # name = "github.com/user/project2" # branch = "dev" # source = "github.com/myfork/project2" # # [[override]] # name = "github.com/x/y" # version = "2.4.0" [[constraint]] name = "github.com/coreos/etcd" version = "3.2.5" [[constraint]] name = "gopkg.in/yaml.v2" version = "2.2.2" vip-manager-0.6/LICENSE000066400000000000000000000024771353774041100146110ustar00rootroot00000000000000BSD 2-Clause License Copyright (c) 2017-2019, Cybertec Schönig & Schönig GmbH All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. vip-manager-0.6/Makefile000066400000000000000000000036271353774041100152420ustar00rootroot00000000000000NAME=vip-manager VERSION=0.6-1 ARCH=amd64 LICENSE="BSD 2-Clause License" MAINTAINER="Ants Aasma " DESCRIPTION="Manages a virtual IP based on state kept in etcd/consul." HOMEPAGE="http://www.cybertec.at/" GIT="git://github.com/cybertec-postgresql/vip-manager.git" GITBROWSER="https://github.com/cybertec-postgresql/vip-manager" all: vip-manager vip-manager: *.go */*.go go build -ldflags="-s -w" . install: install -d $(DESTDIR)/usr/bin install vip-manager $(DESTDIR)/usr/bin/vip-manager install -d $(DESTDIR)/lib/systemd/system install package/scripts/init-systemd.service $(DESTDIR)/lib/systemd/system/vip-manager.service install -d $(DESTDIR)/etc/init.d/ install package/scripts/init-systemv.sh $(DESTDIR)/etc/init.d/vip-manager install -d $(DESTDIR)/etc/default install vipconfig/vip-manager.yml $(DESTDIR)/etc/default/vip-manager.yml DESTDIR=tmp .PHONY: package package: package-deb package-rpm package-deb: vip-manager install -d $(DESTDIR)/usr/bin install vip-manager $(DESTDIR)/usr/bin/vip-manager install -d $(DESTDIR)/usr/share/doc/$(NAME) install --mode=644 package/DEBIAN/copyright $(DESTDIR)/usr/share/doc/$(NAME)/copyright fpm -f -s dir -t deb -n $(NAME) -v $(VERSION) -C $(DESTDIR) \ -p $(NAME)_$(VERSION)_$(ARCH).deb \ --license $(LICENSE) \ --maintainer $(MAINTAINER) \ --vendor $(MAINTAINER) \ --description $(DESCRIPTION) \ --url $(HOMEPAGE) \ --deb-field 'Vcs-Git: $(GIT)' \ --deb-field 'Vcs-Browser: $(GITBROWSER)' \ --deb-upstream-changelog package/DEBIAN/changelog \ --deb-no-default-config-files \ --deb-default vipconfig/vip-manager.yml \ --deb-systemd package/scripts/vip-manager.service \ usr/bin usr/share/doc/ package-rpm: package-deb fpm -f -s deb -t rpm -n $(NAME) -v $(VERSION) -C $(DESTDIR) \ -p $(NAME)_$(VERSION)_$(ARCH).rpm \ $(NAME)_$(VERSION)_$(ARCH).deb clean: rm -f vip-manager rm -f vip-manager*.deb rm -f vip-manager*.rpm rm -fr $(DESTDIR) vip-manager-0.6/README.md000066400000000000000000000055321353774041100150560ustar00rootroot00000000000000# vip-manager Manages a virtual IP based on state kept in etcd or Consul. Monitors state in etcd ## building 1. To make sure that internal includes (the vipconfig and the checker package) are satisfied, place the base directory of this project properly into your `$GOPATH`. The resulting location should be `$GOPATH/src/github.com/cybertec-postgresql/vip-manager/`. The easiest way to do this is: ```go get github.com/cybertec-postgresql/vip-manager``` 2. Build the binary using `make`. 3. To build your own .deb or .rpm, `fpm` is required. Install it, add it to your path and try running `make package`, which will generate a .deb package and will also convert that into a matching .rpm file. > note: on debianoids, rpmbuild will be required to create the rpm package... ## Installing on debian * Install the debian package. Currently you will have to build the package yourself. Prebuilt packages are coming soon. * Edit `/etc/default/vip-manager`. See the configuration section for details. * Start and enable vip-manager service with systemctl. ## Installing by hand * Build the vip-manager binary using go. * Install service file from `package/scripts/vip-manager.service` to `/etc/systemd/system/` * Install configuration file from `package/config/vip-manager.default` to `/etc/default/vip-manager` * Edit config and start the service. ## Configuration The configuration can be passed to the executable through argument flags or through a YAML config file. > The location of the YAML config file can be specified with the -config flag. > An exemplary config file is installed into `/etc/default/vip-manager_default.yml` or is available in the vipconfig directory in the repository of the software. These configuration keys are currently mandatory: | Variable | Example | Description | |-----------|----------|------------------------------------------------------------------------------------------| | VIP_IP | 10.1.2.3 | Virtual IP that is being managed | | VIP_MASK | 24 | Netmask of the virtual IP | | VIP_IFACE | eth0 | Network interface to configure the IP address on. Usually the primary network interface. | | VIP_KEY | /service/batman/leader | Key to monitor. Must match scope from Patroni postgres.yml | | VIP_HOST | serverX | Value to trigger on. Must match name from Patroni. | | VIP_TYPE | etcd | Type of endpoint (etcd or consul) | | VIP_ENDPOINT | http://10.1.2.3:2379 | Location of endpoint (etcd or consul) | ## Author Cybertec Schönig & Schönig GmbH, https://www.cybertec-postgresql.com vip-manager-0.6/basicConfigurer.go000066400000000000000000000072041353774041100172310ustar00rootroot00000000000000package main import ( "bufio" "fmt" "log" "net" "os/exec" "strings" "syscall" "time" arp "github.com/mdlayher/arp" ) /** * The BasicConfigurer can be used to enable vip-management on nodes * that handle their own network connection, in setups where it is * sufficient to add the virtual ip using `ip addr add ...` . * After adding the virtual ip to the specified interface, * a gratuitous ARP package is sent out to update the tables of * nearby routers and other devices. */ const ( arpReplyOp = 2 ) type BasicConfigurer struct { *IPConfiguration arpClient *arp.Client } func NewBasicConfigurer(config *IPConfiguration) (*BasicConfigurer, error) { c := &BasicConfigurer{IPConfiguration: config} err := c.createArpClient() if err != nil { log.Fatalf("Couldn't create an Arp client: %s", err) } return c, nil } func (c *BasicConfigurer) createArpClient() error { var err error var arpClient *arp.Client for i := 0; i < c.Retry_num; i++ { arpClient, err = arp.Dial(&c.iface) if err != nil { log.Printf("Problems with producing the arp client: %s", err) } else { break } time.Sleep(time.Duration(c.Retry_after) * time.Millisecond) } if err != nil { log.Print("too many retries") return err } c.arpClient = arpClient return nil } func (c *BasicConfigurer) ARPSendGratuitous() error { gratuitousPackage, err := arp.NewPacket( arpReplyOp, c.iface.HardwareAddr, c.vip, ethernetBroadcast, net.IPv4bcast, ) if err != nil { log.Printf("Gratuitous arp package is malformed: %s", err) return err } for i := 0; i < c.Retry_num; i++ { err = c.arpClient.WriteTo(gratuitousPackage, ethernetBroadcast) if err != nil { log.Printf("Couldn't write to the arpClient: %s", err) err = c.createArpClient() } else { break } time.Sleep(time.Duration(c.Retry_after) * time.Millisecond) } if err != nil { log.Print("too many retries") return err } return nil } func (c *BasicConfigurer) QueryAddress() bool { cmd := exec.Command("ip", "addr", "show", c.iface.Name) lookup := fmt.Sprintf("inet %s", c.GetCIDR()) result := false stdout, err := cmd.StdoutPipe() if err != nil { panic(err) } err = cmd.Start() if err != nil { panic(err) } scn := bufio.NewScanner(stdout) for scn.Scan() { line := scn.Text() if strings.Contains(line, lookup) { result = true } } cmd.Wait() return result } func (c *BasicConfigurer) ConfigureAddress() bool { log.Printf("Configuring address %s on %s", c.GetCIDR(), c.iface.Name) result := c.runAddressConfiguration("add") if result == true { // For now it is save to say that also working even if a // gratuitous arp message could not be send but logging an // errror should be enough. c.ARPSendGratuitous() } return result } func (c *BasicConfigurer) DeconfigureAddress() bool { log.Printf("Removing address %s on %s", c.GetCIDR(), c.iface.Name) return c.runAddressConfiguration("delete") } func (c *BasicConfigurer) runAddressConfiguration(action string) bool { cmd := exec.Command("ip", "addr", action, c.GetCIDR(), "dev", c.iface.Name) err := cmd.Run() switch exit := err.(type) { case *exec.ExitError: if status, ok := exit.Sys().(syscall.WaitStatus); ok { if status.ExitStatus() == 2 { // Already exists return true } else { log.Printf("Got error %s", status) } } return false } if err != nil { log.Printf("Error running ip address %s %s on %s: %s", action, c.vip, c.iface.Name, err) return false } return true } func (c *BasicConfigurer) GetCIDR() string { return fmt.Sprintf("%s/%d", c.vip.String(), NetmaskSize(c.netmask)) } func (c *BasicConfigurer) cleanupArp() { c.arpClient.Close() } vip-manager-0.6/checker/000077500000000000000000000000001353774041100151765ustar00rootroot00000000000000vip-manager-0.6/checker/consul_leader_checker.go000066400000000000000000000037071353774041100220370ustar00rootroot00000000000000package checker import ( "context" "log" "net/url" "time" "github.com/cybertec-postgresql/vip-manager/vipconfig" "github.com/hashicorp/consul/api" ) type ConsulLeaderChecker struct { key string nodename string apiClient *api.Client } //naming this c_conf to avoid conflict with conf in etcd_leader_checker.go var c_conf vipconfig.Config //func NewConsulLeaderChecker(endpoint, key, nodename string) (*ConsulLeaderChecker, error) { func NewConsulLeaderChecker(con vipconfig.Config) (*ConsulLeaderChecker, error) { c_conf = con lc := &ConsulLeaderChecker{ key: c_conf.Key, nodename: c_conf.Nodename, } url, err := url.Parse(c_conf.Endpoints[0]) if err != nil { return nil, err } address := url.Hostname() + ":" + url.Port() config := &api.Config{ Address: address, Scheme: url.Scheme, WaitTime: time.Second, } if c_conf.Consul_token != ""{ config.Token = c_conf.Consul_token } apiClient, err := api.NewClient(config) if err != nil { return nil, err } lc.apiClient = apiClient return lc, nil } func (c *ConsulLeaderChecker) GetChangeNotificationStream(ctx context.Context, out chan<- bool) error { kv := c.apiClient.KV() queryOptions := &api.QueryOptions{ RequireConsistent: true, } checkLoop: for { resp, _, err := kv.Get(c.key, queryOptions) if err != nil { if ctx.Err() != nil { break checkLoop } log.Printf("consul error: %s", err) out <- false time.Sleep(time.Duration(c_conf.Interval) * time.Millisecond) continue } if resp == nil { log.Printf("Cannot get variable for key %s. Will try again in a second.", c.key) out <- false time.Sleep(time.Duration(c_conf.Interval) * time.Millisecond) continue } state := string(resp.Value) == c.nodename queryOptions.WaitIndex = resp.ModifyIndex select { case <-ctx.Done(): break checkLoop case out <- state: time.Sleep(time.Duration(c_conf.Interval) * time.Millisecond) continue } } return ctx.Err() } vip-manager-0.6/checker/etcd_leader_checker.go000066400000000000000000000032271353774041100214500ustar00rootroot00000000000000package checker import ( "context" "log" "time" "github.com/coreos/etcd/client" "github.com/cybertec-postgresql/vip-manager/vipconfig" ) type EtcdLeaderChecker struct { key string nodename string kapi client.KeysAPI } //naming this c_conf to avoid conflict with conf in etcd_leader_checker.go var e_conf vipconfig.Config //func NewEtcdLeaderChecker(endpoint, key, nodename string, etcd_user string, etcd_password string) (*EtcdLeaderChecker, error) { func NewEtcdLeaderChecker(con vipconfig.Config) (*EtcdLeaderChecker, error) { e_conf = con e := &EtcdLeaderChecker{key: e_conf.Key, nodename: e_conf.Nodename} cfg := client.Config{ Endpoints: e_conf.Endpoints, Transport: client.DefaultTransport, HeaderTimeoutPerRequest: time.Second, Username: e_conf.Etcd_user, Password: e_conf.Etcd_password, } c, err := client.New(cfg) if err != nil { return nil, err } e.kapi = client.NewKeysAPI(c) return e, nil } func (e *EtcdLeaderChecker) GetChangeNotificationStream(ctx context.Context, out chan<- bool) error { clientOptions := &client.GetOptions{ Quorum: true, Recursive: false, } checkLoop: for { resp, err := e.kapi.Get(ctx, e.key, clientOptions) if err != nil { if ctx.Err() != nil { break checkLoop } log.Printf("etcd error: %s", err) out <- false time.Sleep(time.Duration(e_conf.Interval) * time.Millisecond) continue } state := resp.Node.Value == e.nodename select { case <-ctx.Done(): break checkLoop case out <- state: time.Sleep(time.Duration(e_conf.Interval) * time.Millisecond) continue } } return ctx.Err() } vip-manager-0.6/checker/leader_checker.go000066400000000000000000000013441353774041100204470ustar00rootroot00000000000000package checker import ( "context" "errors" "github.com/cybertec-postgresql/vip-manager/vipconfig" ) var ErrUnsupportedEndpointType = errors.New("given endpoint type not supported") type LeaderChecker interface { GetChangeNotificationStream(ctx context.Context, out chan<- bool) error } //func NewLeaderChecker(endpointType, endpoint, key, nodename string, etcd_user string, etcd_password string) (LeaderChecker, error) { func NewLeaderChecker(con vipconfig.Config) (LeaderChecker, error) { var lc LeaderChecker var err error switch con.Endpoint_type { case "consul": lc, err = NewConsulLeaderChecker(con) case "etcd": lc, err = NewEtcdLeaderChecker(con) default: err = ErrUnsupportedEndpointType } return lc, err } vip-manager-0.6/hetznerConfigurer.go000066400000000000000000000160221353774041100176250ustar00rootroot00000000000000package main import ( "bufio" "encoding/json" "errors" "fmt" "log" "net" "os" "os/exec" "time" ) /** * The HetznerConfigurer can be used to enable vip-management on nodes * rented in a Hetzner Datacenter. * Since Hetzner provides an API that handles failover-ip routing, * this API is used to manage the vip, whenever hostintype `hetzner` is set. */ const ( UNKNOWN = iota // c0 == 0 CONFIGURED = iota // c1 == 1 RELEASED = iota // c2 == 2 ) type HetznerConfigurer struct { *IPConfiguration cachedState int lastAPICheck time.Time } func NewHetznerConfigurer(config *IPConfiguration) (*HetznerConfigurer, error) { c := &HetznerConfigurer{IPConfiguration: config, cachedState: UNKNOWN, lastAPICheck: time.Unix(0, 0)} return c, nil } /** * In order to tell the Hetzner API to route the failover-ip to * this machine, we must attach our own IP address to the API request. */ func getOutboundIP() net.IP { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil || conn == nil { log.Println("error dialing 8.8.8.8 to retrieve preferred outbound IP", err) return nil } defer conn.Close() localAddr := conn.LocalAddr().(*net.UDPAddr) return localAddr.IP } func (c *HetznerConfigurer) curlQueryFailover(post bool) (string, error) { /** * The credentials for the API are loaded from a file stored in /etc/hetzner . */ //TODO: make credentialsFile dynamically changeable? credentialsFile := "/etc/hetzner" f, err := os.Open(credentialsFile) if err != nil { log.Println("can't open passwordfile", err) return "", err } defer f.Close() /** * The retrieval of username and password from the file is rather static, * so the credentials file must conform to the offsets down below perfectly. */ var user string var password string scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() switch line[:4] { case "user": user = line[6 : len(line)-1] case "pass": password = line[6 : len(line)-1] } } if user == "" || password == "" { log.Println("Couldn't retrieve username or password from file", credentialsFile) return "", errors.New("Couldn't retrieve username or password from file") } /** * As Hetzner API only allows IPv4 connections, we rely on curl * instead of GO's own http package, * as selecting IPv4 transport there doesn't seem trivial. * * If post is set to true, a failover will be triggered. * If it is set to false, the current state (i.e. route) * for the failover-ip will be retrieved. */ var cmd *exec.Cmd if post == true { my_own_ip := getOutboundIP() if my_own_ip == nil { log.Printf("Error determining this machine's IP address.") return "", errors.New("Error determining this machine's IP address") } log.Printf("my_own_ip: %s\n", my_own_ip.String()) cmd = exec.Command("curl", "--ipv4", "-u", user+":"+password, "https://robot-ws.your-server.de/failover/"+c.vip.String(), "-d", "active_server_ip="+my_own_ip.String()) } else { cmd = exec.Command("curl", "--ipv4", "-u", user+":"+password, "https://robot-ws.your-server.de/failover/"+c.vip.String()) } out, err := cmd.Output() if err != nil { return "", err } retStr := string(out[:]) return retStr, nil } /** * This function is used to parse the response which comes from the * curlQueryFailover function and in turn from the curl calls to the API. */ func getActiveIpFromJson(str string) (net.IP, error) { var f map[string]interface{} err := json.Unmarshal([]byte(str), &f) if err != nil { log.Println(err) return nil, err } if f["error"] != nil { errormap := f["error"].(map[string]interface{}) log.Printf("There was an error accessing the Hetzner API!\n status: %s\n code: %s\n message: %s\n", errormap["status"].(float64), errormap["code"].(string), errormap["message"].(string)) return nil, errors.New("Hetzner API returned error response.") } if f["failover"] != nil { failovermap := f["failover"].(map[string]interface{}) ip := failovermap["ip"].(string) netmask := failovermap["netmask"].(string) server_ip := failovermap["server_ip"].(string) server_number := failovermap["server_number"].(float64) active_server_ip := failovermap["active_server_ip"].(string) log.Println("Result of the failover query was: ", "failover-ip=", ip, "netmask=", netmask, "server_ip=", server_ip, "server_number=", server_number, "active_server_ip=", active_server_ip, ) return net.ParseIP(active_server_ip), nil } return nil, errors.New("why did we end up here?") } func (c *HetznerConfigurer) QueryAddress() bool { if (time.Since(c.lastAPICheck) / time.Hour) > 1 { /**We need to recheck the status! * Don't check too often because of stupid API rate limits */ log.Println("Cached state was too old.") c.cachedState = UNKNOWN } else { /** no need to check, we can use "cached" state if set. * if it is set to UNKOWN, a check will be done. */ if c.cachedState == CONFIGURED { return true } else if c.cachedState == RELEASED { return false } } str, err := c.curlQueryFailover(false) if err != nil { //TODO c.cachedState = UNKNOWN } else { c.lastAPICheck = time.Now() } currentFailoverDestinationIP, err := getActiveIpFromJson(str) if err != nil { //TODO c.cachedState = UNKNOWN } if currentFailoverDestinationIP.Equal(getOutboundIP()) { //We "are" the current failover destination. c.cachedState = CONFIGURED return true } else { c.cachedState = RELEASED } return false } func (c *HetznerConfigurer) ConfigureAddress() bool { //log.Printf("Configuring address %s on %s", m.GetCIDR(), m.iface.Name) return c.runAddressConfiguration("set") } func (c *HetznerConfigurer) DeconfigureAddress() bool { //The adress doesn't need deconfiguring since Hetzner API // is used to point the VIP adress somewhere else. c.cachedState = RELEASED return true } func (c *HetznerConfigurer) runAddressConfiguration(action string) bool { str, err := c.curlQueryFailover(true) if err != nil { log.Printf("Error while configuring Hetzner failover-ip! errormessage: %s", err) c.cachedState = UNKNOWN return false } currentFailoverDestinationIP, err := getActiveIpFromJson(str) if err != nil { c.cachedState = UNKNOWN return false } c.lastAPICheck = time.Now() if currentFailoverDestinationIP.Equal(getOutboundIP()) { //We "are" the current failover destination. log.Printf("Failover was successfully executed!") c.cachedState = CONFIGURED return true } else { log.Printf("The failover command was issued, but the current Failover destination (%s) is different from what it should be (%s).", currentFailoverDestinationIP.String(), getOutboundIP().String) //Something must have gone wrong while trying to switch IP's... c.cachedState = UNKNOWN return false } return true } func (c *HetznerConfigurer) GetCIDR() string { return fmt.Sprintf("%s/%d", c.vip.String(), NetmaskSize(c.netmask)) } func (c *HetznerConfigurer) cleanupArp() { // dummy function as the usage of interfaces requires us to have this function. // It is sufficient for the leader to tell Hetzner to switch the IP, no cleanup needed. } vip-manager-0.6/ip_configuration.go000066400000000000000000000006441353774041100174640ustar00rootroot00000000000000package main import ( "fmt" "net" ) type IPConfiguration struct { vip net.IP netmask net.IPMask iface net.Interface Retry_num int Retry_after int } func (c *IPConfiguration) GetCIDR() string { return fmt.Sprintf("%s/%d", c.vip.String(), NetmaskSize(c.netmask)) } func NetmaskSize(mask net.IPMask) int { ones, bits := mask.Size() if bits == 0 { panic("Invalid mask") } return ones } vip-manager-0.6/ip_manager.go000066400000000000000000000046751353774041100162370ustar00rootroot00000000000000package main import ( "context" "log" "net" "sync" "time" ) var ( ethernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ) type IPConfigurer interface { QueryAddress() bool ConfigureAddress() bool DeconfigureAddress() bool GetCIDR() string cleanupArp() } type IPManager struct { configurer IPConfigurer states <-chan bool currentState bool stateLock sync.Mutex recheck *sync.Cond } func NewIPManager(hostingType string, config *IPConfiguration, states <-chan bool) (*IPManager, error) { m := &IPManager{ states: states, currentState: false, } m.recheck = sync.NewCond(&m.stateLock) switch hostingType { case "hetzner": c, err := NewHetznerConfigurer(config) if err != nil { return nil, err } m.configurer = c case "basic": fallthrough default: c, err := NewBasicConfigurer(config) if err != nil { return nil, err } m.configurer = c } return m, nil } func (m *IPManager) applyLoop(ctx context.Context) { for { actualState := m.configurer.QueryAddress() m.stateLock.Lock() desiredState := m.currentState log.Printf("IP address %s state is %t, desired %t", m.configurer.GetCIDR(), actualState, desiredState) if actualState != desiredState { m.stateLock.Unlock() var configureState bool = false if desiredState { configureState = m.configurer.ConfigureAddress() } else { configureState = m.configurer.DeconfigureAddress() } if configureState != true { log.Printf("Error while acquiring virtual ip for this machine") //Sleep a little bit to avoid busy waiting due to the for loop. time.Sleep(time.Duration(10) * time.Second) } } else { // Wait for notification m.recheck.Wait() // Want to query actual state anyway, so unlock m.stateLock.Unlock() // Check if we should exit select { case <-ctx.Done(): m.configurer.DeconfigureAddress() return default: } } } } func (m *IPManager) SyncStates(ctx context.Context, states <-chan bool) { ticker := time.NewTicker(10 * time.Second) var wg sync.WaitGroup wg.Add(1) go func() { m.applyLoop(ctx) wg.Done() }() for { select { case newState := <-states: m.stateLock.Lock() if m.currentState != newState { m.currentState = newState m.recheck.Broadcast() } m.stateLock.Unlock() case <-ticker.C: m.recheck.Broadcast() case <-ctx.Done(): m.recheck.Broadcast() wg.Wait() m.configurer.cleanupArp() return } } } vip-manager-0.6/main.go000066400000000000000000000107661353774041100150570ustar00rootroot00000000000000package main import ( "context" "flag" "fmt" "io/ioutil" "log" "net" "os" "os/signal" "sync" "gopkg.in/yaml.v2" "github.com/cybertec-postgresql/vip-manager/checker" "github.com/cybertec-postgresql/vip-manager/vipconfig" //"github.com/milosgajdos83/tenus" ) var configFile = flag.String("config", "", "Location of the configuration file.") var versionHint = flag.Bool("version", false, "Show the version number.") // deprecated flags below. add new parameters to the config struct and write them into vip-manager.yml var ip = flag.String("ip", "none", "Virtual IP address to configure") var mask = flag.Int("mask", -1, "The netmask used for the IP address. Defaults to -1 which assigns ipv4 default mask.") var iface = flag.String("iface", "none", "Network interface to configure on") var key = flag.String("key", "none", "key to monitor, e.g. /service/batman/leader") var host = flag.String("host", "none", "Value to monitor for") var etcd_user = flag.String("etcd_user", "none", "username that can be used to access the key in etcd") var etcd_password = flag.String("etcd_password", "none", "password for the etcd_user") var endpointType = flag.String("type", "etcd", "type of endpoint used for key storage. Supported values: etcd, consul") var endpoint = flag.String("endpoint", "http://localhost:2379[,http://host:port,..]", "endpoint") var interval = flag.Int("interval", 1000, "DCS scan interval in milliseconds") var hostingType = flag.String("hostingtype", "basic", "type of hosting. Supported values: self, hetzner") var conf vipconfig.Config func checkFlag(f string, name string) { if f == "none" || f == "" { log.Fatalf("Setting %s is mandatory", name) } } func getMask(vip net.IP, mask int) net.IPMask { if mask > 0 || mask < 33 { return net.CIDRMask(mask, 32) } return vip.DefaultMask() } func getNetIface(iface string) *net.Interface { netIface, err := net.InterfaceByName(iface) if err != nil { log.Fatalf("Obtaining the interface raised an error: %s", err) } return netIface } func main() { flag.Parse() if *versionHint == true { fmt.Println("version 0.6.1") return } //introduce parsed values into conf conf = vipconfig.Config{Ip: *ip, Mask: *mask, Iface: *iface, HostingType: *hostingType, Key: *key, Nodename: *host, Endpoint_type: *endpointType, Endpoints: []string{*endpoint}, Etcd_user: *etcd_user, Etcd_password: *etcd_password, Interval: *interval} if *configFile != "" { yamlFile, err := ioutil.ReadFile(*configFile) if err != nil { log.Fatal("couldn't open config File!", err) } log.Printf("reading config from %s", *configFile) err = yaml.Unmarshal(yamlFile, &conf) if err != nil { log.Fatalf("Error while reading config file: %v", err) } } else { log.Printf("No config file specified, using arguments only.") } checkFlag(conf.Ip, "IP") checkFlag(conf.Iface, "network interface") checkFlag(conf.Key, "key") if len(conf.Endpoints) == 0 { log.Print("No etcd/consul endpoints specified, trying to use localhost with standard ports!") switch conf.Endpoint_type { case "consul": conf.Endpoints[0] = "http://127.0.0.1:2379" case "etcd": conf.Endpoints[0] = "http://127.0.0.1:8500" } } if conf.Nodename == "" { nodename, err := os.Hostname() if err != nil { log.Fatalf("No nodename specified, hostname could not be retrieved: %s", err) } else { log.Printf("No nodename specified, instead using hostname: %v", nodename) conf.Nodename = nodename } } states := make(chan bool) lc, err := checker.NewLeaderChecker(conf) if err != nil { log.Fatalf("Failed to initialize leader checker: %s", err) } vip := net.ParseIP(conf.Ip) vipMask := getMask(vip, conf.Mask) netIface := getNetIface(conf.Iface) manager, err := NewIPManager( *hostingType, &IPConfiguration{ vip: vip, netmask: vipMask, iface: *netIface, Retry_num: conf.Retry_num, Retry_after: conf.Retry_after, }, states, ) if err != nil { log.Fatalf("Problems with generating the virtual ip manager: %s", err) } mainCtx, cancel := context.WithCancel(context.Background()) go func() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c log.Printf("Received exit signal") cancel() }() var wg sync.WaitGroup wg.Add(1) go func() { err := lc.GetChangeNotificationStream(mainCtx, states) if err != nil && err != context.Canceled { log.Fatalf("Leader checker returned the following error: %s", err) } wg.Done() }() wg.Add(1) go func() { manager.SyncStates(mainCtx, states) wg.Done() }() wg.Wait() } vip-manager-0.6/package/000077500000000000000000000000001353774041100151655ustar00rootroot00000000000000vip-manager-0.6/package/DEBIAN/000077500000000000000000000000001353774041100161075ustar00rootroot00000000000000vip-manager-0.6/package/DEBIAN/changelog000066400000000000000000000030401353774041100177560ustar00rootroot00000000000000vip-manager (0.6-1) unstable; urgency=low * switched to yaml file for config, still supporting all command line options from previous releases. * Introduced unified config management in the vipconfig source directory * Enabled consul_leader_checker to use tokens -- Julian Markwort Tue, 04 Sep 2018 14:40:00 +0300 vip-manager (0.5-1) unstable; urgency=low * Support Hetzner API for failover-ip configuration. * Enable etcd-checker to use username and password. -- Julian Markwort Tue, 04 Sep 2018 14:40:00 +0300 vip-manager (0.4-1) unstable; urgency=low * Fix SystemD service file and add netmask parameter to it. * Remove SystemV init script from packaging. Apparently debians systemd does not like it. -- Ants Aasma Thu, 19 Apr 2018 01:18:00 +0300 vip-manager (0.3-0) unstable; urgency=low * The vip-manager is sending a gratuitous arp if it takes over the vip * It will be no longer checked if another server has the vip because it makes no difference as long as the new server sends a gratuitous arp message * Fixed the vip-manager packaging -- David Leib Wed, 25 Oct 2017 14:25:44 +0300 vip-manager (0.2-0) unstable; urgency=low * The vip-manager is not failing anymore if the keystorage is not available -- David Leib Thu, 21 Sep 2017 12:23:34 +0300 vip-manager (0.1-1) unstable; urgency=low * Initial release -- Ants Aasma Tue, 25 Apr 2017 15:58:29 +0300 vip-manager-0.6/package/DEBIAN/compat000066400000000000000000000000021353774041100173050ustar00rootroot000000000000009 vip-manager-0.6/package/DEBIAN/copyright000066400000000000000000000030001353774041100200330ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: vip-manager Source: Files: * Copyright: 2017-2019 Ants Aasma License: BSD-2-clause All rights reserved. . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. vip-manager-0.6/package/DEBIAN/rules000077500000000000000000000000371353774041100171670ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ vip-manager-0.6/package/DEBIAN/source/000077500000000000000000000000001353774041100174075ustar00rootroot00000000000000vip-manager-0.6/package/DEBIAN/source/format000066400000000000000000000000141353774041100206150ustar00rootroot000000000000003.0 (quilt) vip-manager-0.6/package/config/000077500000000000000000000000001353774041100164325ustar00rootroot00000000000000vip-manager-0.6/package/config/vip-manager.default000066400000000000000000000010121353774041100222000ustar00rootroot00000000000000# All keys here are mandatory #VIP_IP="10.1.2.3" # Netmask for IP address #VIP_MASK=24 # Just use the normal interface name of the primary network interface #VIP_IFACE="eth0" # This must match scope from Patroni postgres.yml #VIP_KEY="/service/batman/leader" # This value must match the value used in Patroni postgres.yml #VIP_HOST="serverX" # Specify the type of endpoint (etcd|consul) #VIP_TYPE="etcd" #VIP_ENDPOINT="http://10.1.2.3:2379" #not mandatory #VIP_HOSTINGTYPE="basic" #VIP_ETCD_USER #VIP_ETCD_PASSWORDvip-manager-0.6/package/scripts/000077500000000000000000000000001353774041100166545ustar00rootroot00000000000000vip-manager-0.6/package/scripts/vip-manager000066400000000000000000000116231353774041100210100ustar00rootroot00000000000000#! /usr/bin/env bash # chkconfig: 2345 99 01 # description: Vip-manager daemon ### BEGIN INIT INFO # Provides: vip-manager # Required-Start: $network $local_fs $remote_fs # Required-Stop: $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start telegraf at boot time ### END INIT INFO # Command-line options that will be set with /etc/default/vip-manager. VIP_OPTS="" USER=root GROUP=root if [ -r /lib/lsb/init-functions ]; then source /lib/lsb/init-functions fi DEFAULT=/etc/default/vip-manager if [ -r $DEFAULT ]; then source $DEFAULT fi if [ -z "$VIP_IP" ]; then VIP_OPTS="$VIP_OPTS -ip=$VIP_IP" fi if [ -z "$VIP_IFACE" ]; then VIP_OPTS="$VIP_OPTS -iface=$VIP_IFACE" fi if [ -z "$VIP_KEY" ]; then VIP_OPTS="$VIP_OPTS -key=$VIP_KEY" fi if [ -z "$VIP_ETCD_USER" ]; then VIP_OPTS="$VIP_OPTS -etcd_user=$VIP_ETCD_USER" fi if [ -z "$VIP_ETCD_PASSWORD" ]; then VIP_OPTS="$VIP_OPTS -etcd_password=$VIP_ETCD_USER" fi if [ -z "$VIP_HOST" ]; then VIP_OPTS="$VIP_OPTS -host=$VIP_HOST" fi if [ -z "$VIP_TYPE" ]; then VIP_OPTS="$VIP_OPTS -type=$VIP_TYPE" fi if [ -z "$VIP_ENDPOINT" ]; then VIP_OPTS="$VIP_OPTS -type=$VIP_ENDPOINT" fi if [ -z "$VIP_MASK" ]; then VIP_OPTS="$VIP_OPTS -type=$VIP_MASK" fi if [ -z "$VIP_HOSTINGTYPE" ]; then VIP_OPTS="$VIP_OPTS -hostingtype=$VIP_HOSTINGTYPE" fi if [ -z "$STDOUT" ]; then STDOUT=/dev/null fi if [ ! -f "$STDOUT" ]; then mkdir -p `dirname $STDOUT` fi if [ -z "$STDERR" ]; then STDERR=/var/log/vipmanager/vipmanager.log fi if [ ! -f "$STDERR" ]; then mkdir -p `dirname $STDERR` fi function pidofproc { if [ $# -ne 3 ]; then echo "Expected three arguments, e.g. $0 -p pidfile daemon-name" fi if [ ! -f "$2" ]; then return 1 fi local pidfile=`cat $2` if [ "x$pidfile" == "x" ]; then return 1 fi if ps --pid "$pidfile" | grep -q $(basename $3); then return 0 fi return 1 } function killproc { if [ $# -ne 3 ]; then echo "Expected three arguments, e.g. $0 -p pidfile signal" fi pid=`cat $2` kill -s $3 $pid } function log_failure_msg { echo "$@" "[ FAILED ]" } function log_success_msg { echo "$@" "[ OK ]" } # Process name ( For display ) name=vipmanager # Daemon name, where is the actual executable daemon=/usr/bin/vip-manager # pid file for the daemon pidfile=/var/run/vip-manager.pid piddir=`dirname $pidfile` if [ ! -d "$piddir" ]; then mkdir -p $piddir chown $USER:$GROUP $piddir fi # If the daemon is not there, then exit. [ -x $daemon ] || exit 5 case $1 in start) # Checked the PID file exists and check the actual status of process if [ -e $pidfile ]; then pidofproc -p $pidfile $daemon > /dev/null 2>&1 && status="0" || status="$?" # If the status is SUCCESS then don't need to start again. if [ "x$status" = "x0" ]; then log_failure_msg "$name process is running" exit 0 # Exit fi fi log_success_msg "Starting the process" "$name" if command -v startproc >/dev/null; then startproc -u "$USER" -g "$GROUP" -p "$pidfile" -q -- "$daemon" $VIP_OPTS elif which start-stop-daemon > /dev/null 2>&1; then start-stop-daemon --chuid $USER:$GROUP --start --quiet --pidfile $pidfile --exec $daemon -- $VIP_OPTS >>$STDOUT 2>>$STDERR & else su -s /bin/sh -c "nohup $daemon $VIP_OPTS >>$STDOUT 2>>$STDERR &" $USER fi log_success_msg "$name process was started" ;; stop) # Stop the daemon. if [ -e $pidfile ]; then pidofproc -p $pidfile $daemon > /dev/null 2>&1 && status="0" || status="$?" if [ "$status" = 0 ]; then if killproc -p $pidfile SIGTERM && /bin/rm -rf $pidfile; then log_success_msg "$name process was stopped" else log_failure_msg "$name failed to stop service" fi fi else log_failure_msg "$name process is not running" fi ;; restart|force-reload) # Restart the daemon. $0 stop && sleep 2 && $0 start ;; status) # Check the status of the process. if [ -e $pidfile ]; then if pidofproc -p $pidfile $daemon > /dev/null; then log_success_msg "$name Process is running" exit 0 else log_failure_msg "$name Process is not running" exit 1 fi else log_failure_msg "$name Process is not running" exit 3 fi ;; version) $daemon version ;; *) # For invalid arguments, print the usage message. echo "Usage: $0 {start|stop|restart|status|version}" exit 2 ;; esac vip-manager-0.6/package/scripts/vip-manager.service000066400000000000000000000003401353774041100224410ustar00rootroot00000000000000 [Unit] Description=Manages Virtual IP for Patroni Before=patroni.service [Service] Type=simple ExecStart=/usr/bin/vip-manager -config=/etc/default/vip-manager.yml Restart=on-failure [Install] WantedBy=multi-user.target vip-manager-0.6/vendor/000077500000000000000000000000001353774041100150675ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/000077500000000000000000000000001353774041100171265ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/000077500000000000000000000000001353774041100207335ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/arp/000077500000000000000000000000001353774041100215155ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/arp/.travis.yml000066400000000000000000000005321353774041100236260ustar00rootroot00000000000000language: go go: - 1.x before_install: - go get github.com/axw/gocov/gocov - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover before_script: - go get -d ./... script: - go test -v ./... - if ! $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN; then echo "Coveralls not available."; fi vip-manager-0.6/vendor/github.com/mdlayher/arp/LICENSE.md000066400000000000000000000020701353774041100231200ustar00rootroot00000000000000MIT License =========== Copyright (C) 2015 Matt Layher 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. vip-manager-0.6/vendor/github.com/mdlayher/arp/README.md000066400000000000000000000012101353774041100227660ustar00rootroot00000000000000arp [![Build Status](https://travis-ci.org/mdlayher/arp.svg?branch=master)](https://travis-ci.org/mdlayher/arp) [![Coverage Status](https://coveralls.io/repos/mdlayher/arp/badge.svg?branch=master)](https://coveralls.io/r/mdlayher/arp?branch=master) [![GoDoc](http://godoc.org/github.com/mdlayher/arp?status.svg)](http://godoc.org/github.com/mdlayher/arp) === Package `arp` implements the ARP protocol, as described in RFC 826. MIT Licensed. Portions of this code are taken from the Go standard library. The Go standard library is Copyright (c) 2012 The Go Authors. All rights reserved. The Go license can be found at https://golang.org/LICENSE. vip-manager-0.6/vendor/github.com/mdlayher/arp/client.go000066400000000000000000000154021353774041100233240ustar00rootroot00000000000000package arp import ( "errors" "net" "time" "github.com/mdlayher/ethernet" "github.com/mdlayher/raw" ) var ( // errNoIPv4Addr is returned when an interface does not have an IPv4 // address. errNoIPv4Addr = errors.New("no IPv4 address available for interface") ) // A Client is an ARP client, which can be used to send and receive // ARP packets. type Client struct { ifi *net.Interface ip net.IP p net.PacketConn } // Dial creates a new Client using the specified network interface. // Dial retrieves the IPv4 address of the interface and binds a raw socket // to send and receive ARP packets. func Dial(ifi *net.Interface) (*Client, error) { // Open raw socket to send and receive ARP packets using ethernet frames // we build ourselves p, err := raw.ListenPacket(ifi, raw.ProtocolARP) if err != nil { return nil, err } return New(ifi, p) } // New creates a new Client using the specified network interface // and net.PacketConn. This allows the caller to define exactly how they bind to the // net.PacketConn. This is most useful to define what protocol to pass to socket(7). // // In most cases, callers would be better off calling Dial. func New(ifi *net.Interface, p net.PacketConn) (*Client, error) { // Check for usable IPv4 addresses for the Client addrs, err := ifi.Addrs() if err != nil { return nil, err } return newClient(ifi, p, addrs) } // newClient is the internal, generic implementation of newClient. It is used // to allow an arbitrary net.PacketConn to be used in a Client, so testing // is easier to accomplish. func newClient(ifi *net.Interface, p net.PacketConn, addrs []net.Addr) (*Client, error) { ip, err := firstIPv4Addr(addrs) if err != nil { return nil, err } return &Client{ ifi: ifi, ip: ip, p: p, }, nil } // Close closes the Client's raw socket and stops sending and receiving // ARP packets. func (c *Client) Close() error { return c.p.Close() } // Request sends an ARP request, asking for the hardware address // associated with an IPv4 address. The response, if any, can be read // with the Read method. // // Unlike Resolve, which provides an easier interface for getting the // hardware address, Request allows sending many requests in a row, // retrieving the responses afterwards. func (c *Client) Request(ip net.IP) error { if c.ip == nil { return errNoIPv4Addr } // Create ARP packet for broadcast address to attempt to find the // hardware address of the input IP address arp, err := NewPacket(OperationRequest, c.ifi.HardwareAddr, c.ip, ethernet.Broadcast, ip) if err != nil { return err } return c.WriteTo(arp, ethernet.Broadcast) } // Resolve performs an ARP request, attempting to retrieve the // hardware address of a machine using its IPv4 address. Resolve must not // be used concurrently with Read. If you're using Read (usually in a // loop), you need to use Request instead. Resolve may read more than // one message if it receives messages unrelated to the request. func (c *Client) Resolve(ip net.IP) (net.HardwareAddr, error) { err := c.Request(ip) if err != nil { return nil, err } // Loop and wait for replies for { arp, _, err := c.Read() if err != nil { return nil, err } if arp.Operation != OperationReply || !arp.SenderIP.Equal(ip) { continue } return arp.SenderHardwareAddr, nil } } // Read reads a single ARP packet and returns it, together with its // ethernet frame. func (c *Client) Read() (*Packet, *ethernet.Frame, error) { buf := make([]byte, 128) for { n, _, err := c.p.ReadFrom(buf) if err != nil { return nil, nil, err } p, eth, err := parsePacket(buf[:n]) if err != nil { if err == errInvalidARPPacket { continue } return nil, nil, err } return p, eth, nil } } // WriteTo writes a single ARP packet to addr. Note that addr should, // but doesn't have to, match the target hardware address of the ARP // packet. func (c *Client) WriteTo(p *Packet, addr net.HardwareAddr) error { pb, err := p.MarshalBinary() if err != nil { return err } f := ðernet.Frame{ Destination: p.TargetHardwareAddr, Source: p.SenderHardwareAddr, EtherType: ethernet.EtherTypeARP, Payload: pb, } fb, err := f.MarshalBinary() if err != nil { return err } _, err = c.p.WriteTo(fb, &raw.Addr{HardwareAddr: addr}) return err } // Reply constructs and sends a reply to an ARP request. On the ARP // layer, it will be addressed to the sender address of the packet. On // the ethernet layer, it will be sent to the actual remote address // from which the request was received. // // For more fine-grained control, use WriteTo to write a custom // response. func (c *Client) Reply(req *Packet, hwAddr net.HardwareAddr, ip net.IP) error { p, err := NewPacket(OperationReply, hwAddr, ip, req.SenderHardwareAddr, req.SenderIP) if err != nil { return err } return c.WriteTo(p, req.SenderHardwareAddr) } // Copyright (c) 2012 The Go Authors. All rights reserved. // Source code in this file is based on src/net/interface_linux.go, // from the Go standard library. The Go license can be found here: // https://golang.org/LICENSE. // Documentation taken from net.PacketConn interface. Thanks: // http://golang.org/pkg/net/#PacketConn. // SetDeadline sets the read and write deadlines associated with the // connection. func (c *Client) SetDeadline(t time.Time) error { return c.p.SetDeadline(t) } // SetReadDeadline sets the deadline for future raw socket read calls. // If the deadline is reached, a raw socket read will fail with a timeout // (see type net.Error) instead of blocking. // A zero value for t means a raw socket read will not time out. func (c *Client) SetReadDeadline(t time.Time) error { return c.p.SetReadDeadline(t) } // SetWriteDeadline sets the deadline for future raw socket write calls. // If the deadline is reached, a raw socket write will fail with a timeout // (see type net.Error) instead of blocking. // A zero value for t means a raw socket write will not time out. // Even if a write times out, it may return n > 0, indicating that // some of the data was successfully written. func (c *Client) SetWriteDeadline(t time.Time) error { return c.p.SetWriteDeadline(t) } // HardwareAddr fetches the hardware address for the interface associated // with the connection. func (c Client) HardwareAddr() net.HardwareAddr { return c.ifi.HardwareAddr } // firstIPv4Addr attempts to retrieve the first detected IPv4 address from an // input slice of network addresses. func firstIPv4Addr(addrs []net.Addr) (net.IP, error) { for _, a := range addrs { if a.Network() != "ip+net" { continue } ip, _, err := net.ParseCIDR(a.String()) if err != nil { return nil, err } // "If ip is not an IPv4 address, To4 returns nil." // Reference: http://golang.org/pkg/net/#IP.To4 if ip4 := ip.To4(); ip4 != nil { return ip4, nil } } return nil, nil } vip-manager-0.6/vendor/github.com/mdlayher/arp/client_request_test.go000066400000000000000000000167631353774041100261460ustar00rootroot00000000000000package arp import ( "bytes" "errors" "io" "net" "testing" ) func TestClientRequestNoIPv4Address(t *testing.T) { c := &Client{} _, got := c.Resolve(net.IPv4zero) if want := errNoIPv4Addr; want != got { t.Fatalf("unexpected error for no IPv4 address:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestInvalidSourceHardwareAddr(t *testing.T) { c := &Client{ ifi: &net.Interface{}, ip: net.IPv4zero, } _, got := c.Resolve(net.IPv4zero) if want := ErrInvalidHardwareAddr; want != got { t.Fatalf("unexpected error for invalid source hardware address:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestIPv6Address(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, } _, got := c.Resolve(net.IPv6loopback) if want := ErrInvalidIP; want != got { t.Fatalf("unexpected error for IPv6 address:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestErrWriteTo(t *testing.T) { errWriteTo := errors.New("test error") c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, p: &errWriteToPacketConn{ err: errWriteTo, }, } _, got := c.Resolve(net.IPv4zero) if want := errWriteTo; want != got { t.Fatalf("unexpected error during WriteTo:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestErrReadFrom(t *testing.T) { errReadFrom := errors.New("test error") c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, p: &errReadFromPacketConn{ err: errReadFrom, }, } _, got := c.Resolve(net.IPv4zero) if want := errReadFrom; want != got { t.Fatalf("unexpected error during ReadFrom:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestEthernetFrameUnexpectedEOF(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, p: &bufferReadFromPacketConn{ b: bytes.NewBuffer([]byte{0}), }, } _, got := c.Resolve(net.IPv4zero) if want := io.ErrUnexpectedEOF; want != got { t.Fatalf("unexpected error while reading ethernet frame:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestEthernetFrameWrongDestinationHardwareAddr(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}, }, ip: net.IPv4zero, p: &bufferReadFromPacketConn{ b: bytes.NewBuffer(append([]byte{ // Ethernet frame with wrong destination hardware address 0, 0, 0, 0, 0, 0, // Wrong destination 0, 0, 0, 0, 0, 0, 0x00, 0x00, }, make([]byte, 46)...)), }, } _, got := c.Resolve(net.IPv4zero) if want := io.EOF; want != got { t.Fatalf("unexpected error while reading ethernet frame with wrong destination hardware address:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestEthernetFrameWrongEtherType(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, p: &bufferReadFromPacketConn{ b: bytes.NewBuffer(append([]byte{ // Ethernet frame with non-ARP EtherType 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, // Wrong EtherType }, make([]byte, 46)...)), }, } _, got := c.Resolve(net.IPv4zero) if want := io.EOF; want != got { t.Fatalf("unexpected error while reading ethernet frame with wrong EtherType:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestARPPacketUnexpectedEOF(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, p: &bufferReadFromPacketConn{ b: bytes.NewBuffer(append([]byte{ // Ethernet frame 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x06, // ARP packet with misleading hardware address length 0, 0, 0, 0, 255, 255, // Misleading hardware address length }, make([]byte, 40)...)), }, } _, got := c.Resolve(net.IPv4zero) if want := io.ErrUnexpectedEOF; want != got { t.Fatalf("unexpected error while reading ARP packet:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestARPRequestInsteadOfResponse(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4zero, p: &bufferReadFromPacketConn{ b: bytes.NewBuffer(append([]byte{ // Ethernet frame 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x06, // ARP request, not response 0, 1, 0x08, 0x06, 6, 4, 0, 1, // Request, not Response 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, make([]byte, 46)...)), }, } _, got := c.Resolve(net.IPv4zero) if want := io.EOF; want != got { t.Fatalf("unexpected error while reading ARP response with wrong operation type:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestARPResponseWrongSenderIP(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 0, 0, 0, 0, 0}, }, ip: net.IPv4(192, 168, 1, 1).To4(), p: &bufferReadFromPacketConn{ b: bytes.NewBuffer(append([]byte{ // Ethernet frame 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x06, // ARP Packet not bound for this IP address 0, 1, 0x08, 0x06, 6, 4, 0, 2, 0, 0, 0, 0, 0, 0, 192, 168, 1, 10, // Wrong IP address 0, 0, 0, 0, 0, 0, 192, 168, 1, 1, }, make([]byte, 46)...)), }, } _, got := c.Resolve(net.IPv4zero) if want := io.EOF; want != got { t.Fatalf("unexpected error while reading ARP response with wrong sender IP:\n- want: %v\n- got: %v", want, got) } } func TestClientRequestOK(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}, }, ip: net.IPv4(192, 168, 1, 1).To4(), p: &bufferReadFromPacketConn{ b: bytes.NewBuffer(append([]byte{ // Ethernet frame 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x08, 0x06, // ARP Packet 0, 1, 0x08, 0x06, 6, 4, 0, 2, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 192, 168, 1, 10, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, // mac needn't match ours 192, 168, 1, 2, // ip needn't match ours }, make([]byte, 40)...)), }, } wantHW := net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff} gotHW, err := c.Resolve(net.IPv4(192, 168, 1, 10)) if err != nil { t.Fatal(err) } if want, got := wantHW, gotHW; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address for request:\n- want: %v\n- got: %v", want, got) } } // bufferReadFromPacketConn is a net.PacketConn which copies bytes from its // embedded buffer into b when when its ReadFrom method is called. type bufferReadFromPacketConn struct { b *bytes.Buffer noopPacketConn } func (p *bufferReadFromPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { n, err := p.b.Read(b) return n, nil, err } // errWriteToPacketConn is a net.PacketConn which always returns its embedded // error when its WriteTo method is called. type errWriteToPacketConn struct { err error noopPacketConn } func (p *errWriteToPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { return 0, p.err } // errReadFromPacketConn is a net.PacketConn which always returns its embedded // error when its ReadFrom method is called. type errReadFromPacketConn struct { err error noopPacketConn } func (p *errReadFromPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, p.err } vip-manager-0.6/vendor/github.com/mdlayher/arp/client_test.go000066400000000000000000000137501353774041100243670ustar00rootroot00000000000000package arp import ( "bytes" "net" "reflect" "testing" "time" ) func TestClientClose(t *testing.T) { p := &closeCapturePacketConn{} c := &Client{p: p} if err := c.Close(); err != nil { t.Fatal(err) } if !p.closed { t.Fatal("client was not closed") } } func TestClientSetDeadline(t *testing.T) { p := &deadlineCapturePacketConn{} c := &Client{p: p} d := time.Now() if err := c.SetDeadline(d); err != nil { t.Fatal(err) } if want, got := d, p.r; want != got { t.Fatalf("unexpected read deadline: %v != %v", want, got) } if want, got := d, p.w; want != got { t.Fatalf("unexpected write deadline: %v != %v", want, got) } } func TestClientSetReadDeadline(t *testing.T) { p := &deadlineCapturePacketConn{} c := &Client{p: p} d := time.Now() if err := c.SetReadDeadline(d); err != nil { t.Fatal(err) } if want, got := d, p.r; want != got { t.Fatalf("unexpected read deadline: %v != %v", want, got) } if want, got := (time.Time{}), p.w; want != got { t.Fatalf("non-zero write deadline: %v", got) } } func TestClientSetWriteDeadline(t *testing.T) { p := &deadlineCapturePacketConn{} c := &Client{p: p} d := time.Now() if err := c.SetWriteDeadline(d); err != nil { t.Fatal(err) } if want, got := (time.Time{}), p.r; want != got { t.Fatalf("non-zero read deadline: %v", got) } if want, got := d, p.w; want != got { t.Fatalf("unexpected write deadline: %v != %v", want, got) } } func TestClientHardwareAddr(t *testing.T) { c := &Client{ ifi: &net.Interface{ HardwareAddr: net.HardwareAddr{0, 1, 2, 3, 4, 5}, }, } if want, got := c.ifi.HardwareAddr.String(), c.HardwareAddr().String(); want != got { t.Fatalf("unexpected hardware address: %v != %v", want, got) } } func Test_newClient(t *testing.T) { var tests = []struct { desc string addrs []net.Addr c *Client err error }{ { desc: "no network addresses", c: &Client{}, }, { desc: "OK", addrs: []net.Addr{ &net.IPNet{ IP: net.IPv4(192, 168, 1, 1), Mask: []byte{255, 255, 255, 0}, }, }, c: &Client{ ip: net.IPv4(192, 168, 1, 1).To4(), }, }, } for i, tt := range tests { c, err := newClient(nil, nil, tt.addrs) if err != nil { if want, got := tt.err.Error(), err.Error(); want != got { t.Fatalf("[%02d] test %q, unexpected error: %v != %v", i, tt.desc, want, got) } continue } if want, got := tt.c, c; !reflect.DeepEqual(want, got) { t.Fatalf("[%02d] test %q, unexpected Client: %v != %v", i, tt.desc, want, got) } } } func Test_firstIPv4Addr(t *testing.T) { var tests = []struct { desc string addrs []net.Addr ip net.IP err error }{ { desc: "no network addresses", }, { desc: "non-IP network", addrs: []net.Addr{ &net.UnixAddr{ Name: "foo.sock", Net: "unix", }, }, }, { desc: "bad CIDR address", addrs: []net.Addr{ &net.IPNet{ IP: net.IPv4(192, 168, 1, 1), }, }, err: &net.ParseError{ Type: "CIDR address", Text: "", }, }, { desc: "IPv6 address only", addrs: []net.Addr{ &net.IPNet{ IP: net.IPv6loopback, Mask: []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, }, }, }, }, { desc: "IPv4 address only", addrs: []net.Addr{ &net.IPNet{ IP: net.IPv4(192, 168, 1, 1), Mask: []byte{255, 255, 255, 0}, }, }, ip: net.IPv4(192, 168, 1, 1), }, { desc: "IPv4 and IPv6 addresses", addrs: []net.Addr{ &net.IPNet{ IP: net.IPv6loopback, Mask: []byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, }, }, &net.IPNet{ IP: net.IPv4(192, 168, 1, 1), Mask: []byte{255, 255, 255, 0}, }, }, ip: net.IPv4(192, 168, 1, 1), }, { desc: "multiple IPv4 addresses", addrs: []net.Addr{ &net.IPNet{ IP: net.IPv4(10, 0, 0, 1), Mask: []byte{255, 0, 0, 0}, }, &net.IPNet{ IP: net.IPv4(192, 168, 1, 1), Mask: []byte{255, 255, 255, 0}, }, }, ip: net.IPv4(10, 0, 0, 1), }, } for i, tt := range tests { ip, err := firstIPv4Addr(tt.addrs) if err != nil { if want, got := tt.err.Error(), err.Error(); want != got { t.Fatalf("[%02d] test %q, unexpected error: %v != %v", i, tt.desc, want, got) } continue } if want, got := tt.ip.To4(), ip.To4(); !bytes.Equal(want, got) { t.Fatalf("[%02d] test %q, unexpected IPv4 address: %v != %v", i, tt.desc, want, got) } } } // closeCapturePacketConn is a net.PacketConn which captures when // it is closed. type closeCapturePacketConn struct { closed bool noopPacketConn } func (p *closeCapturePacketConn) Close() error { p.closed = true return nil } // deadlineCapturePacketConn is a net.PacketConn which captures read and // write deadlines. type deadlineCapturePacketConn struct { r time.Time w time.Time noopPacketConn } func (p *deadlineCapturePacketConn) SetDeadline(t time.Time) error { p.r = t p.w = t return nil } func (p *deadlineCapturePacketConn) SetReadDeadline(t time.Time) error { p.r = t return nil } func (p *deadlineCapturePacketConn) SetWriteDeadline(t time.Time) error { p.w = t return nil } // noopPacketConn is a net.PacketConn which simply no-ops any input. It is // embedded in other implementations so they do not have to implement every // single method. type noopPacketConn struct{} func (noopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, nil } func (noopPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { return 0, nil } func (noopPacketConn) Close() error { return nil } func (noopPacketConn) LocalAddr() net.Addr { return nil } func (noopPacketConn) SetDeadline(t time.Time) error { return nil } func (noopPacketConn) SetReadDeadline(t time.Time) error { return nil } func (noopPacketConn) SetWriteDeadline(t time.Time) error { return nil } func (noopPacketConn) HardwareAddr() net.HardwareAddr { return nil } vip-manager-0.6/vendor/github.com/mdlayher/arp/cmd/000077500000000000000000000000001353774041100222605ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/arp/cmd/arpc/000077500000000000000000000000001353774041100232055ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/arp/cmd/arpc/README.md000066400000000000000000000007241353774041100244670ustar00rootroot00000000000000arpc ===== Command `arpc` provides a simple ARP client which can be used to retrieve hardware addresses of other machines in a LAN using their IPv4 addresses. Usage ----- ``` $ ./arpc -h Usage of ./arpc: -d=1s: timeout for ARP request -i="eth0": network interface to use for ARP request -ip="": IPv4 address destination for ARP request ``` Request hardware address for IPv4 address: ``` $ ./arpc -i eth0 -ip 192.168.1.1 192.168.1.1 -> 00:12:7f:eb:6b:40 ``` vip-manager-0.6/vendor/github.com/mdlayher/arp/cmd/arpc/main.go000066400000000000000000000023631353774041100244640ustar00rootroot00000000000000// Command arpc provides a simple ARP client which can be used to retrieve // hardware addresses of other machines in a LAN using their IPv4 address. package main import ( "flag" "fmt" "log" "net" "time" "github.com/mdlayher/arp" ) var ( // durFlag is used to set a timeout for an ARP request durFlag = flag.Duration("d", 1*time.Second, "timeout for ARP request") // ifaceFlag is used to set a network interface for ARP requests ifaceFlag = flag.String("i", "eth0", "network interface to use for ARP request") // ipFlag is used to set an IPv4 address destination for an ARP request ipFlag = flag.String("ip", "", "IPv4 address destination for ARP request") ) func main() { flag.Parse() // Ensure valid network interface ifi, err := net.InterfaceByName(*ifaceFlag) if err != nil { log.Fatal(err) } // Set up ARP client with socket c, err := arp.Dial(ifi) if err != nil { log.Fatal(err) } // Set request deadline from flag if err := c.SetDeadline(time.Now().Add(*durFlag)); err != nil { log.Fatal(err) } // Request hardware address for IP address ip := net.ParseIP(*ipFlag).To4() mac, err := c.Resolve(ip) if err != nil { log.Fatal(err) } fmt.Printf("%s -> %s", ip, mac) // Clean up ARP client socket _ = c.Close() } vip-manager-0.6/vendor/github.com/mdlayher/arp/cmd/proxyarpd/000077500000000000000000000000001353774041100243105ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/arp/cmd/proxyarpd/main.go000066400000000000000000000033511353774041100255650ustar00rootroot00000000000000package main import ( "bytes" "flag" "io" "log" "net" "github.com/mdlayher/arp" "github.com/mdlayher/ethernet" ) var ( // ifaceFlag is used to set a network interface for ARP traffic ifaceFlag = flag.String("i", "eth0", "network interface to use for ARP traffic") // ipFlag is used to set an IPv4 address to proxy ARP on behalf of ipFlag = flag.String("ip", "", "IP address for device to proxy ARP on behalf of") ) func main() { flag.Parse() // Ensure valid interface and IPv4 address ifi, err := net.InterfaceByName(*ifaceFlag) if err != nil { log.Fatal(err) } ip := net.ParseIP(*ipFlag).To4() if ip == nil { log.Fatalf("invalid IPv4 address: %q", *ipFlag) } client, err := arp.Dial(ifi) if err != nil { log.Fatalf("couldn't create ARP client: %s", err) } // Handle ARP requests bound for designated IPv4 address, using proxy ARP // to indicate that the address belongs to this machine for { pkt, eth, err := client.Read() if err != nil { if err == io.EOF { log.Println("EOF") break } log.Fatalf("error processing ARP requests: %s", err) } // Ignore ARP replies if pkt.Operation != arp.OperationRequest { continue } // Ignore ARP requests which are not broadcast or bound directly for // this machine if !bytes.Equal(eth.Destination, ethernet.Broadcast) && !bytes.Equal(eth.Destination, ifi.HardwareAddr) { continue } log.Printf("request: who-has %s? tell %s (%s)", pkt.TargetIP, pkt.SenderIP, pkt.SenderHardwareAddr) // Ignore ARP requests which do not indicate the target IP if !pkt.TargetIP.Equal(ip) { continue } log.Printf(" reply: %s is-at %s", ip, ifi.HardwareAddr) if err := client.Reply(pkt, ifi.HardwareAddr, ip); err != nil { log.Fatal(err) } } } vip-manager-0.6/vendor/github.com/mdlayher/arp/doc.go000066400000000000000000000001211353774041100226030ustar00rootroot00000000000000// Package arp implements the ARP protocol, as described in RFC 826. package arp vip-manager-0.6/vendor/github.com/mdlayher/arp/fuzz.go000066400000000000000000000003311353774041100230370ustar00rootroot00000000000000// +build gofuzz package arp func Fuzz(data []byte) int { p := new(Packet) if err := p.UnmarshalBinary(data); err != nil { return 0 } if _, err := p.MarshalBinary(); err != nil { panic(err) } return 1 } vip-manager-0.6/vendor/github.com/mdlayher/arp/packet.go000066400000000000000000000151251353774041100233170ustar00rootroot00000000000000package arp import ( "encoding/binary" "errors" "io" "net" "github.com/mdlayher/ethernet" ) var ( // ErrInvalidHardwareAddr is returned when one or more invalid hardware // addresses are passed to NewPacket. ErrInvalidHardwareAddr = errors.New("invalid hardware address") // ErrInvalidIP is returned when one or more invalid IPv4 addresses are // passed to NewPacket. ErrInvalidIP = errors.New("invalid IPv4 address") // errInvalidARPPacket is returned when an ethernet frame does not // indicate that an ARP packet is contained in its payload. errInvalidARPPacket = errors.New("invalid ARP packet") ) //go:generate stringer -output=string.go -type=Operation // An Operation is an ARP operation, such as request or reply. type Operation uint16 // Operation constants which indicate an ARP request or reply. const ( OperationRequest Operation = 1 OperationReply Operation = 2 ) // A Packet is a raw ARP packet, as described in RFC 826. type Packet struct { // HardwareType specifies an IANA-assigned hardware type, as described // in RFC 826. HardwareType uint16 // ProtocolType specifies the internetwork protocol for which the ARP // request is intended. Typically, this is the IPv4 EtherType. ProtocolType uint16 // HardwareAddrLength specifies the length of the sender and target // hardware addresses included in a Packet. HardwareAddrLength uint8 // IPLength specifies the length of the sender and target IPv4 addresses // included in a Packet. IPLength uint8 // Operation specifies the ARP operation being performed, such as request // or reply. Operation Operation // SenderHardwareAddr specifies the hardware address of the sender of this // Packet. SenderHardwareAddr net.HardwareAddr // SenderIP specifies the IPv4 address of the sender of this Packet. SenderIP net.IP // TargetHardwareAddr specifies the hardware address of the target of this // Packet. TargetHardwareAddr net.HardwareAddr // TargetIP specifies the IPv4 address of the target of this Packet. TargetIP net.IP } // NewPacket creates a new Packet from an input Operation and hardware/IPv4 // address values for both a sender and target. // // If either hardware address is less than 6 bytes in length, or there is a // length mismatch between the two, ErrInvalidHardwareAddr is returned. // // If either IP address is not an IPv4 address, or there is a length mismatch // between the two, ErrInvalidIP is returned. func NewPacket(op Operation, srcHW net.HardwareAddr, srcIP net.IP, dstHW net.HardwareAddr, dstIP net.IP) (*Packet, error) { // Validate hardware addresses for minimum length, and matching length if len(srcHW) < 6 { return nil, ErrInvalidHardwareAddr } if len(dstHW) < 6 { return nil, ErrInvalidHardwareAddr } if len(srcHW) != len(dstHW) { return nil, ErrInvalidHardwareAddr } // Validate IP addresses to ensure they are IPv4 addresses, and // correct length srcIP = srcIP.To4() if srcIP == nil { return nil, ErrInvalidIP } dstIP = dstIP.To4() if dstIP == nil { return nil, ErrInvalidIP } return &Packet{ // There is no Go-native way to detect hardware type of a network // interface, so default to 1 (ethernet 10Mb) for now HardwareType: 1, // Default to EtherType for IPv4 ProtocolType: uint16(ethernet.EtherTypeIPv4), // Populate other fields using input data HardwareAddrLength: uint8(len(srcHW)), IPLength: uint8(len(srcIP)), Operation: op, SenderHardwareAddr: srcHW, SenderIP: srcIP, TargetHardwareAddr: dstHW, TargetIP: dstIP, }, nil } // MarshalBinary allocates a byte slice containing the data from a Packet. // // MarshalBinary never returns an error. func (p *Packet) MarshalBinary() ([]byte, error) { // 2 bytes: hardware type // 2 bytes: protocol type // 1 byte : hardware address length // 1 byte : protocol length // 2 bytes: operation // N bytes: source hardware address // N bytes: source protocol address // N bytes: target hardware address // N bytes: target protocol address // Though an IPv4 address should always 4 bytes, go-fuzz // very quickly created several crasher scenarios which // indicated that these values can lie. b := make([]byte, 2+2+1+1+2+(p.IPLength*2)+(p.HardwareAddrLength*2)) // Marshal fixed length data binary.BigEndian.PutUint16(b[0:2], p.HardwareType) binary.BigEndian.PutUint16(b[2:4], p.ProtocolType) b[4] = p.HardwareAddrLength b[5] = p.IPLength binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation)) // Marshal variable length data at correct offset using lengths // defined in p n := 8 hal := int(p.HardwareAddrLength) pl := int(p.IPLength) copy(b[n:n+hal], p.SenderHardwareAddr) n += hal copy(b[n:n+pl], p.SenderIP) n += pl copy(b[n:n+hal], p.TargetHardwareAddr) n += hal copy(b[n:n+pl], p.TargetIP) return b, nil } // UnmarshalBinary unmarshals a raw byte slice into a Packet. func (p *Packet) UnmarshalBinary(b []byte) error { // Must have enough room to retrieve hardware address and IP lengths if len(b) < 8 { return io.ErrUnexpectedEOF } // Retrieve fixed length data p.HardwareType = binary.BigEndian.Uint16(b[0:2]) p.ProtocolType = binary.BigEndian.Uint16(b[2:4]) p.HardwareAddrLength = b[4] p.IPLength = b[5] p.Operation = Operation(binary.BigEndian.Uint16(b[6:8])) // Unmarshal variable length data at correct offset using lengths // defined by ml and il // // These variables are meant to improve readability of offset calculations // for the code below n := 8 ml := int(p.HardwareAddrLength) ml2 := ml * 2 il := int(p.IPLength) il2 := il * 2 // Must have enough room to retrieve both hardware address and IP addresses addrl := n + ml2 + il2 if len(b) < addrl { return io.ErrUnexpectedEOF } // Allocate single byte slice to store address information, which // is resliced into fields bb := make([]byte, addrl-n) // Sender hardware address copy(bb[0:ml], b[n:n+ml]) p.SenderHardwareAddr = bb[0:ml] n += ml // Sender IP address copy(bb[ml:ml+il], b[n:n+il]) p.SenderIP = bb[ml : ml+il] n += il // Target hardware address copy(bb[ml+il:ml2+il], b[n:n+ml]) p.TargetHardwareAddr = bb[ml+il : ml2+il] n += ml // Target IP address copy(bb[ml2+il:ml2+il2], b[n:n+il]) p.TargetIP = bb[ml2+il : ml2+il2] return nil } func parsePacket(buf []byte) (*Packet, *ethernet.Frame, error) { f := new(ethernet.Frame) if err := f.UnmarshalBinary(buf); err != nil { return nil, nil, err } // Ignore frames which do not have ARP EtherType if f.EtherType != ethernet.EtherTypeARP { return nil, nil, errInvalidARPPacket } p := new(Packet) if err := p.UnmarshalBinary(f.Payload); err != nil { return nil, nil, err } return p, f, nil } vip-manager-0.6/vendor/github.com/mdlayher/arp/packet_test.go000066400000000000000000000234731353774041100243630ustar00rootroot00000000000000package arp import ( "bytes" "io" "net" "reflect" "testing" "github.com/mdlayher/ethernet" ) func TestNewPacket(t *testing.T) { zeroHW := net.HardwareAddr{0, 0, 0, 0, 0, 0} var tests = []struct { desc string op Operation srcHW net.HardwareAddr srcIP net.IP dstHW net.HardwareAddr dstIP net.IP p *Packet err error }{ { desc: "short source hardware address", srcHW: net.HardwareAddr{0, 0, 0, 0, 0}, err: ErrInvalidHardwareAddr, }, { desc: "short destination hardware address", srcHW: zeroHW, dstHW: net.HardwareAddr{0, 0, 0, 0, 0}, err: ErrInvalidHardwareAddr, }, { desc: "hardware address length mismatch", srcHW: zeroHW, dstHW: net.HardwareAddr{0, 0, 0, 0, 0, 0, 0, 0}, err: ErrInvalidHardwareAddr, }, { desc: "short source IPv4 address", srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IP{0, 0, 0}, err: ErrInvalidIP, }, { desc: "long source IPv4 address", srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IP{0, 0, 0, 0, 0}, err: ErrInvalidIP, }, { desc: "IPv6 source IP address", srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IPv6zero, err: ErrInvalidIP, }, { desc: "short destination IPv4 address", srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IPv4zero, dstIP: net.IP{0, 0, 0}, err: ErrInvalidIP, }, { desc: "long destination IPv4 address", srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IPv4zero, dstIP: net.IP{0, 0, 0, 0, 0}, err: ErrInvalidIP, }, { desc: "IPv6 destination IP address", srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IPv4zero, dstIP: net.IPv6zero, err: ErrInvalidIP, }, { desc: "OK", op: OperationRequest, srcHW: zeroHW, dstHW: zeroHW, srcIP: net.IPv4zero, dstIP: net.IPv4zero, p: &Packet{ HardwareType: 1, ProtocolType: uint16(ethernet.EtherTypeIPv4), HardwareAddrLength: 6, IPLength: 4, Operation: OperationRequest, SenderHardwareAddr: zeroHW, SenderIP: net.IPv4zero.To4(), TargetHardwareAddr: zeroHW, TargetIP: net.IPv4zero.To4(), }, }, } for i, tt := range tests { p, err := NewPacket(tt.op, tt.srcHW, tt.srcIP, tt.dstHW, tt.dstIP) if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("[%02d] test %q, unexpected error: %v != %v", i, tt.desc, want, got) } continue } if want, got := tt.p, p; !reflect.DeepEqual(want, got) { t.Fatalf("[%02d] test %q, unexpected Packet:\n- want: %v\n- got: %v", i, tt.desc, want, got) } } } func TestPacketMarshalBinary(t *testing.T) { zeroHW := net.HardwareAddr{0, 0, 0, 0, 0, 0} ip1 := net.IP{192, 168, 1, 10} ip2 := net.IP{192, 168, 1, 1} iboip1 := net.HardwareAddr(bytes.Repeat([]byte{0}, 20)) iboip2 := net.HardwareAddr(bytes.Repeat([]byte{1}, 20)) var tests = []struct { desc string p *Packet b []byte }{ { desc: "ARP request to ethernet broadcast, 6 byte hardware addresses", p: &Packet{ HardwareType: 1, ProtocolType: uint16(ethernet.EtherTypeIPv4), HardwareAddrLength: 6, IPLength: 4, Operation: OperationRequest, SenderHardwareAddr: zeroHW, SenderIP: ip1, TargetHardwareAddr: ethernet.Broadcast, TargetIP: ip2, }, b: []byte{ 0, 1, 8, 0, 6, 4, 0, 1, 0, 0, 0, 0, 0, 0, 192, 168, 1, 10, 255, 255, 255, 255, 255, 255, 192, 168, 1, 1, }, }, { desc: "ARP reply over infiniband, 20 byte hardware addresses", p: &Packet{ HardwareType: 32, ProtocolType: uint16(ethernet.EtherTypeIPv4), HardwareAddrLength: 20, IPLength: 4, Operation: OperationReply, SenderHardwareAddr: iboip1, SenderIP: ip1, TargetHardwareAddr: iboip2, TargetIP: ip2, }, b: []byte{ 0, 32, 8, 0, 20, 4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 192, 168, 1, 1, }, }, } for i, tt := range tests { b, err := tt.p.MarshalBinary() if err != nil { t.Fatal(err) } if want, got := tt.b, b; !bytes.Equal(want, got) { t.Fatalf("[%02d] test %q, unexpected Packet bytes:\n- want: %v\n- got: %v", i, tt.desc, want, got) } } } func TestPacketUnmarshalBinary(t *testing.T) { zeroHW := net.HardwareAddr{0, 0, 0, 0, 0, 0} ip1 := net.IP{192, 168, 1, 10} ip2 := net.IP{192, 168, 1, 1} iboip1 := net.HardwareAddr(bytes.Repeat([]byte{0}, 20)) iboip2 := net.HardwareAddr(bytes.Repeat([]byte{1}, 20)) var tests = []struct { desc string p *Packet b []byte err error }{ { desc: "short buffer", b: bytes.Repeat([]byte{0}, 7), err: io.ErrUnexpectedEOF, }, { desc: "short buffer, too short for hardware addresses", b: []byte{ 0, 1, 8, 0, 255, 4, 0, 1, }, err: io.ErrUnexpectedEOF, }, { desc: "short buffer, too short for IP addresses", b: []byte{ 0, 1, 8, 0, 6, 255, 0, 1, }, err: io.ErrUnexpectedEOF, }, { desc: "ARP request to ethernet broadcast, 6 byte hardware addresses", b: []byte{ 0, 1, 8, 0, 6, 4, 0, 1, 0, 0, 0, 0, 0, 0, 192, 168, 1, 10, 255, 255, 255, 255, 255, 255, 192, 168, 1, 1, }, p: &Packet{ HardwareType: 1, ProtocolType: uint16(ethernet.EtherTypeIPv4), HardwareAddrLength: 6, IPLength: 4, Operation: OperationRequest, SenderHardwareAddr: zeroHW, SenderIP: ip1, TargetHardwareAddr: ethernet.Broadcast, TargetIP: ip2, }, }, { desc: "ARP reply over infiniband, 20 byte hardware addresses", b: []byte{ 0, 32, 8, 0, 20, 4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 192, 168, 1, 1, }, p: &Packet{ HardwareType: 32, ProtocolType: uint16(ethernet.EtherTypeIPv4), HardwareAddrLength: 20, IPLength: 4, Operation: OperationReply, SenderHardwareAddr: iboip1, SenderIP: ip1, TargetHardwareAddr: iboip2, TargetIP: ip2, }, }, } for i, tt := range tests { p := new(Packet) if err := p.UnmarshalBinary(tt.b); err != nil { if want, got := tt.err, err; want != got { t.Fatalf("[%02d] test %q, unexpected error: %v != %v", i, tt.desc, want, got) } continue } if want, got := tt.p, p; !reflect.DeepEqual(want, got) { t.Fatalf("[%02d] test %q, unexpected Packet bytes:\n- want: %v\n- got: %v", i, tt.desc, want, got) } } } func Test_parsePacket(t *testing.T) { var tests = []struct { desc string buf []byte p *Packet err error }{ { desc: "invalid ethernet frame", err: io.ErrUnexpectedEOF, }, { desc: "non-ARP EtherType", // Approximation of 14 byte ethernet frame header and // 42 byte blank payload (EtherType 0x0000) buf: make([]byte, 56), err: errInvalidARPPacket, }, { desc: "invalid ARP packet", buf: append([]byte{ // Ethernet frame 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x06, // ARP packet with misleading hardware address length 0, 0, 0, 0, 255, 255, // Misleading hardware address length }, make([]byte, 40)...), err: io.ErrUnexpectedEOF, }, { desc: "OK", buf: append([]byte{ // Ethernet frame 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x08, 0x06, // ARP Packet 0, 1, 0x08, 0x06, 6, 4, 0, 2, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 192, 168, 1, 10, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 192, 168, 1, 1, }, make([]byte, 40)...), p: &Packet{ HardwareType: 1, ProtocolType: 2054, HardwareAddrLength: 6, IPLength: 4, Operation: OperationReply, SenderHardwareAddr: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, SenderIP: net.IP{192, 168, 1, 10}, TargetHardwareAddr: net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}, TargetIP: net.IP{192, 168, 1, 1}, }, }, } for i, tt := range tests { p, _, err := parsePacket(tt.buf) if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("[%02d] test %q, unexpected error: %v != %v", i, tt.desc, want, got) } continue } if want, got := tt.p, p; !reflect.DeepEqual(want, got) { t.Fatalf("[%02d] test %q, unexpected Packet:\n- want: %v\n- got: %v", i, tt.desc, want, got) } } } // Benchmarks for Packet.MarshalBinary func BenchmarkPacketMarshalBinary(b *testing.B) { p, err := NewPacket( OperationRequest, net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde}, net.IP{192, 168, 1, 10}, net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}, net.IP{192, 168, 1, 1}, ) if err != nil { b.Fatal(err) } benchmarkPacketMarshalBinary(b, p) } func benchmarkPacketMarshalBinary(b *testing.B, p *Packet) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := p.MarshalBinary(); err != nil { b.Fatal(err) } } } // Benchmarks for Packet.UnmarshalBinary func BenchmarkPacketUnmarshalBinary(b *testing.B) { p, err := NewPacket( OperationRequest, net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde}, net.IP{192, 168, 1, 10}, net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad}, net.IP{192, 168, 1, 1}, ) if err != nil { b.Fatal(err) } benchmarkPacketUnmarshalBinary(b, p) } func benchmarkPacketUnmarshalBinary(b *testing.B, p *Packet) { pb, err := p.MarshalBinary() if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if err := p.UnmarshalBinary(pb); err != nil { b.Fatal(err) } } } vip-manager-0.6/vendor/github.com/mdlayher/arp/string.go000066400000000000000000000006321353774041100233530ustar00rootroot00000000000000// generated by stringer -output=string.go -type=Operation; DO NOT EDIT package arp import "fmt" const _Operation_name = "OperationRequestOperationReply" var _Operation_index = [...]uint8{0, 16, 30} func (i Operation) String() string { i -= 1 if i >= Operation(len(_Operation_index)-1) { return fmt.Sprintf("Operation(%d)", i+1) } return _Operation_name[_Operation_index[i]:_Operation_index[i+1]] } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/000077500000000000000000000000001353774041100225515ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/ethernet/.travis.yml000066400000000000000000000004561353774041100246670ustar00rootroot00000000000000language: go go: - 1.x os: - linux before_install: - go get github.com/golang/lint/golint - go get honnef.co/go/tools/cmd/staticcheck - go get -d ./... script: - go build -tags=gofuzz ./... - go vet ./... - staticcheck ./... - golint -set_exit_status ./... - go test -v -race ./... vip-manager-0.6/vendor/github.com/mdlayher/ethernet/LICENSE.md000066400000000000000000000020701353774041100241540ustar00rootroot00000000000000MIT License =========== Copyright (C) 2015 Matt Layher 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. vip-manager-0.6/vendor/github.com/mdlayher/ethernet/README.md000066400000000000000000000013501353774041100240270ustar00rootroot00000000000000ethernet [![Build Status](https://travis-ci.org/mdlayher/ethernet.svg?branch=master)](https://travis-ci.org/mdlayher/ethernet) [![GoDoc](https://godoc.org/github.com/mdlayher/ethernet?status.svg)](https://godoc.org/github.com/mdlayher/ethernet) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/ethernet)](https://goreportcard.com/report/github.com/mdlayher/ethernet) ======== Package `ethernet` implements marshaling and unmarshaling of IEEE 802.3 Ethernet II frames and IEEE 802.1Q VLAN tags. MIT Licensed. For more information about using Ethernet frames in Go, check out my blog post: [Network Protocol Breakdown: Ethernet and Go](https://medium.com/@mdlayher/network-protocol-breakdown-ethernet-and-go-de985d726cc1).vip-manager-0.6/vendor/github.com/mdlayher/ethernet/cmd/000077500000000000000000000000001353774041100233145ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/ethernet/cmd/etherecho/000077500000000000000000000000001353774041100252625ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/ethernet/cmd/etherecho/README.md000066400000000000000000000021471353774041100265450ustar00rootroot00000000000000etherecho ========= Command `etherecho` broadcasts a message to all machines in the same network segment, and listens for other messages from other `etherecho` servers. `etherecho` only works on Linux and BSD, and requires root permission or `CAP_NET_ADMIN` on Linux. Usage ----- ``` $ etherecho -h Usage of etherecho: -i string network interface to use to send and receive messages -m string message to be sent (default: system's hostname) ``` Example ------- Start an instance of `etherecho` on two machines on the same network segment: ``` foo $ etherecho -i eth0 ``` ``` bar $ etherecho -i eth0 ``` Both machines should begin seeing messages from each other at regular intervals: ``` foo $ etherecho -i eth0 2017/06/14 00:03:13 [aa:aa:aa:aa:aa:aa] bar 2017/06/14 00:03:14 [aa:aa:aa:aa:aa:aa] bar 2017/06/14 00:03:15 [aa:aa:aa:aa:aa:aa] bar ``` ``` bar $ etherecho -i eth0 2017/06/14 00:03:13 [bb:bb:bb:bb:bb:bb] foo 2017/06/14 00:03:14 [bb:bb:bb:bb:bb:bb] foo 2017/06/14 00:03:15 [bb:bb:bb:bb:bb:bb] foo ``` Additional machines can be added, so long as they reside on the same network segment.vip-manager-0.6/vendor/github.com/mdlayher/ethernet/cmd/etherecho/main.go000066400000000000000000000056571353774041100265520ustar00rootroot00000000000000// Command etherecho broadcasts a message to all machines in the same network // segment, and listens for other messages from other etherecho servers. // // etherecho only works on Linux and BSD, and requires root permission or // CAP_NET_ADMIN on Linux. package main import ( "flag" "log" "net" "os" "time" "github.com/mdlayher/ethernet" "github.com/mdlayher/raw" ) // Make use of an unassigned EtherType for etherecho. // https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml const etherType = 0xcccc func main() { var ( ifaceFlag = flag.String("i", "", "network interface to use to send and receive messages") msgFlag = flag.String("m", "", "message to be sent (default: system's hostname)") ) flag.Parse() // Open a raw socket on the specified interface, and configure it to accept // traffic with etherecho's EtherType. ifi, err := net.InterfaceByName(*ifaceFlag) if err != nil { log.Fatalf("failed to find interface %q: %v", *ifaceFlag, err) } c, err := raw.ListenPacket(ifi, etherType) if err != nil { log.Fatalf("failed to listen: %v", err) } // Default message to system's hostname if empty. msg := *msgFlag if msg == "" { msg, err = os.Hostname() if err != nil { log.Fatalf("failed to retrieve hostname: %v", err) } } // Send messages in one goroutine, receive messages in another. go sendMessages(c, ifi.HardwareAddr, msg) go receiveMessages(c, ifi.MTU) // Block forever. select {} } // sendMessages continuously sends a message over a connection at regular intervals, // sourced from specified hardware address. func sendMessages(c net.PacketConn, source net.HardwareAddr, msg string) { // Message is broadcast to all machines in same network segment. f := ðernet.Frame{ Destination: ethernet.Broadcast, Source: source, EtherType: etherType, Payload: []byte(msg), } b, err := f.MarshalBinary() if err != nil { log.Fatalf("failed to marshal ethernet frame: %v", err) } // Required by Linux, even though the Ethernet frame has a destination. // Unused by BSD. addr := &raw.Addr{ HardwareAddr: ethernet.Broadcast, } // Send message forever. t := time.NewTicker(1 * time.Second) for range t.C { if _, err := c.WriteTo(b, addr); err != nil { log.Fatalf("failed to send message: %v", err) } } } // receiveMessages continuously receives messages over a connection. The messages // may be up to the interface's MTU in size. func receiveMessages(c net.PacketConn, mtu int) { var f ethernet.Frame b := make([]byte, mtu) // Keep receiving messages forever. for { n, addr, err := c.ReadFrom(b) if err != nil { log.Fatalf("failed to receive message: %v", err) } // Unpack Ethernet II frame into Go representation. if err := (&f).UnmarshalBinary(b[:n]); err != nil { log.Fatalf("failed to unmarshal ethernet frame: %v", err) } // Display source of message and message itself. log.Printf("[%s] %s", addr.String(), string(f.Payload)) } } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/ethernet.go000066400000000000000000000212321353774041100247160ustar00rootroot00000000000000// Package ethernet implements marshaling and unmarshaling of IEEE 802.3 // Ethernet II frames and IEEE 802.1Q VLAN tags. package ethernet import ( "encoding/binary" "errors" "fmt" "hash/crc32" "io" "net" ) //go:generate stringer -output=string.go -type=EtherType const ( // minPayload is the minimum payload size for an Ethernet frame, assuming // that no 802.1Q VLAN tags are present. minPayload = 46 ) var ( // Broadcast is a special hardware address which indicates a Frame should // be sent to every device on a given LAN segment. Broadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ) var ( // ErrInvalidFCS is returned when Frame.UnmarshalFCS detects an incorrect // Ethernet frame check sequence in a byte slice for a Frame. ErrInvalidFCS = errors.New("invalid frame check sequence") ) // An EtherType is a value used to identify an upper layer protocol // encapsulated in a Frame. // // A list of IANA-assigned EtherType values may be found here: // http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml. type EtherType uint16 // Common EtherType values frequently used in a Frame. const ( EtherTypeIPv4 EtherType = 0x0800 EtherTypeARP EtherType = 0x0806 EtherTypeIPv6 EtherType = 0x86DD // EtherTypeVLAN and EtherTypeServiceVLAN are used as 802.1Q Tag Protocol // Identifiers (TPIDs). EtherTypeVLAN EtherType = 0x8100 EtherTypeServiceVLAN EtherType = 0x88a8 ) // A Frame is an IEEE 802.3 Ethernet II frame. A Frame contains information // such as source and destination hardware addresses, zero or more optional // 802.1Q VLAN tags, an EtherType, and payload data. type Frame struct { // Destination specifies the destination hardware address for this Frame. // // If this address is set to Broadcast, the Frame will be sent to every // device on a given LAN segment. Destination net.HardwareAddr // Source specifies the source hardware address for this Frame. // // Typically, this is the hardware address of the network interface used to // send this Frame. Source net.HardwareAddr // ServiceVLAN specifies an optional 802.1Q service VLAN tag, for use with // 802.1ad double tagging, or "Q-in-Q". If ServiceVLAN is not nil, VLAN must // not be nil as well. // // Most users should leave this field set to nil and use VLAN instead. ServiceVLAN *VLAN // VLAN specifies an optional 802.1Q customer VLAN tag, which may or may // not be present in a Frame. It is important to note that the operating // system may automatically strip VLAN tags before they can be parsed. VLAN *VLAN // EtherType is a value used to identify an upper layer protocol // encapsulated in this Frame. EtherType EtherType // Payload is a variable length data payload encapsulated by this Frame. Payload []byte } // MarshalBinary allocates a byte slice and marshals a Frame into binary form. func (f *Frame) MarshalBinary() ([]byte, error) { b := make([]byte, f.length()) _, err := f.read(b) return b, err } // MarshalFCS allocates a byte slice, marshals a Frame into binary form, and // finally calculates and places a 4-byte IEEE CRC32 frame check sequence at // the end of the slice. // // Most users should use MarshalBinary instead. MarshalFCS is provided as a // convenience for rare occasions when the operating system cannot // automatically generate a frame check sequence for an Ethernet frame. func (f *Frame) MarshalFCS() ([]byte, error) { // Frame length with 4 extra bytes for frame check sequence b := make([]byte, f.length()+4) if _, err := f.read(b); err != nil { return nil, err } // Compute IEEE CRC32 checksum of frame bytes and place it directly // in the last four bytes of the slice binary.BigEndian.PutUint32(b[len(b)-4:], crc32.ChecksumIEEE(b[0:len(b)-4])) return b, nil } // read reads data from a Frame into b. read is used to marshal a Frame // into binary form, but does not allocate on its own. func (f *Frame) read(b []byte) (int, error) { // S-VLAN must also have accompanying C-VLAN. if f.ServiceVLAN != nil && f.VLAN == nil { return 0, ErrInvalidVLAN } copy(b[0:6], f.Destination) copy(b[6:12], f.Source) // Marshal each non-nil VLAN tag into bytes, inserting the appropriate // EtherType/TPID before each, so devices know that one or more VLANs // are present. vlans := []struct { vlan *VLAN tpid EtherType }{ {vlan: f.ServiceVLAN, tpid: EtherTypeServiceVLAN}, {vlan: f.VLAN, tpid: EtherTypeVLAN}, } n := 12 for _, vt := range vlans { if vt.vlan == nil { continue } // Add VLAN EtherType and VLAN bytes. binary.BigEndian.PutUint16(b[n:n+2], uint16(vt.tpid)) if _, err := vt.vlan.read(b[n+2 : n+4]); err != nil { return 0, err } n += 4 } // Marshal actual EtherType after any VLANs, copy payload into // output bytes. binary.BigEndian.PutUint16(b[n:n+2], uint16(f.EtherType)) copy(b[n+2:], f.Payload) return len(b), nil } // UnmarshalBinary unmarshals a byte slice into a Frame. func (f *Frame) UnmarshalBinary(b []byte) error { // Verify that both hardware addresses and a single EtherType are present if len(b) < 14 { return io.ErrUnexpectedEOF } // Track offset in packet for reading data n := 14 // Continue looping and parsing VLAN tags until no more VLAN EtherType // values are detected et := EtherType(binary.BigEndian.Uint16(b[n-2 : n])) switch et { case EtherTypeServiceVLAN, EtherTypeVLAN: // VLAN type is hinted for further parsing. An index is returned which // indicates how many bytes were consumed by VLAN tags. nn, err := f.unmarshalVLANs(et, b[n:]) if err != nil { return err } n += nn default: // No VLANs detected. f.EtherType = et } // Allocate single byte slice to store destination and source hardware // addresses, and payload bb := make([]byte, 6+6+len(b[n:])) copy(bb[0:6], b[0:6]) f.Destination = bb[0:6] copy(bb[6:12], b[6:12]) f.Source = bb[6:12] // There used to be a minimum payload length restriction here, but as // long as two hardware addresses and an EtherType are present, it // doesn't really matter what is contained in the payload. We will // follow the "robustness principle". copy(bb[12:], b[n:]) f.Payload = bb[12:] return nil } // UnmarshalFCS computes the IEEE CRC32 frame check sequence of a Frame, // verifies it against the checksum present in the byte slice, and finally, // unmarshals a byte slice into a Frame. // // Most users should use UnmarshalBinary instead. UnmarshalFCS is provided as // a convenience for rare occasions when the operating system cannot // automatically verify a frame check sequence for an Ethernet frame. func (f *Frame) UnmarshalFCS(b []byte) error { // Must contain enough data for FCS, to avoid panics if len(b) < 4 { return io.ErrUnexpectedEOF } // Verify checksum in slice versus newly computed checksum want := binary.BigEndian.Uint32(b[len(b)-4:]) got := crc32.ChecksumIEEE(b[0 : len(b)-4]) if want != got { return ErrInvalidFCS } return f.UnmarshalBinary(b[0 : len(b)-4]) } // length calculates the number of bytes required to store a Frame. func (f *Frame) length() int { // If payload is less than the required minimum length, we zero-pad up to // the required minimum length pl := len(f.Payload) if pl < minPayload { pl = minPayload } // Add additional length if VLAN tags are needed. var vlanLen int switch { case f.ServiceVLAN != nil && f.VLAN != nil: vlanLen = 8 case f.VLAN != nil: vlanLen = 4 } // 6 bytes: destination hardware address // 6 bytes: source hardware address // N bytes: VLAN tags (if present) // 2 bytes: EtherType // N bytes: payload length (may be padded) return 6 + 6 + vlanLen + 2 + pl } // unmarshalVLANs unmarshals S/C-VLAN tags. It is assumed that tpid // is a valid S/C-VLAN TPID. func (f *Frame) unmarshalVLANs(tpid EtherType, b []byte) (int, error) { // 4 or more bytes must remain for valid S/C-VLAN tag and EtherType. if len(b) < 4 { return 0, io.ErrUnexpectedEOF } // Track how many bytes are consumed by VLAN tags. var n int switch tpid { case EtherTypeServiceVLAN: vlan := new(VLAN) if err := vlan.UnmarshalBinary(b[n : n+2]); err != nil { return 0, err } f.ServiceVLAN = vlan // Assume that a C-VLAN immediately trails an S-VLAN. if EtherType(binary.BigEndian.Uint16(b[n+2:n+4])) != EtherTypeVLAN { return 0, ErrInvalidVLAN } // 4 or more bytes must remain for valid C-VLAN tag and EtherType. n += 4 if len(b[n:]) < 4 { return 0, io.ErrUnexpectedEOF } // Continue to parse the C-VLAN. fallthrough case EtherTypeVLAN: vlan := new(VLAN) if err := vlan.UnmarshalBinary(b[n : n+2]); err != nil { return 0, err } f.VLAN = vlan f.EtherType = EtherType(binary.BigEndian.Uint16(b[n+2 : n+4])) n += 4 default: panic(fmt.Sprintf("unknown VLAN TPID: %04x", tpid)) } return n, nil } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/ethernet_test.go000066400000000000000000000260131353774041100257570ustar00rootroot00000000000000package ethernet import ( "bytes" "io" "net" "reflect" "testing" ) func TestFrameMarshalBinary(t *testing.T) { var tests = []struct { desc string f *Frame b []byte err error }{ { desc: "S-VLAN, no C-VLAN", f: &Frame{ // Contents don't matter. ServiceVLAN: &VLAN{}, }, err: ErrInvalidVLAN, }, { desc: "IPv4, no VLANs", f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, b: append([]byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)...), }, { desc: "IPv6, C-VLAN: (PRI 1, ID 101)", f: &Frame{ Destination: net.HardwareAddr{1, 0, 1, 0, 1, 0}, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeIPv6, Payload: bytes.Repeat([]byte{0}, 50), }, b: append([]byte{ 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0x81, 0x00, 0x20, 0x65, 0x86, 0xDD, }, bytes.Repeat([]byte{0}, 50)...), }, { desc: "ARP, S-VLAN: (PRI 0, DROP, ID 100), C-VLAN: (PRI 1, ID 101)", f: &Frame{ Destination: Broadcast, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, ServiceVLAN: &VLAN{ DropEligible: true, ID: 100, }, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeARP, Payload: bytes.Repeat([]byte{0}, 50), }, b: append([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 0, 1, 0, 1, 0x88, 0xa8, 0x10, 0x64, 0x81, 0x00, 0x20, 0x65, 0x08, 0x06, }, bytes.Repeat([]byte{0}, 50)...), }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { b, err := tt.f.MarshalBinary() if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.b, b; !bytes.Equal(want, got) { t.Fatalf("unexpected Frame bytes:\n- want: %v\n- got: %v", want, got) } }) } } func TestFrameMarshalFCS(t *testing.T) { var tests = []struct { desc string f *Frame b []byte err error }{ { desc: "IPv4, no VLANs", f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, b: append( append( []byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)..., ), []byte{159, 205, 24, 60}..., ), }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { b, err := tt.f.MarshalFCS() if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.b, b; !bytes.Equal(want, got) { t.Fatalf("unexpected Frame bytes:\n- want: %v\n- got: %v", want, got) } }) } } func TestFrameUnmarshalBinary(t *testing.T) { var tests = []struct { desc string b []byte f *Frame err error }{ { desc: "nil buffer", err: io.ErrUnexpectedEOF, }, { desc: "short buffer", b: bytes.Repeat([]byte{0}, 13), err: io.ErrUnexpectedEOF, }, { desc: "1 short S-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0xa8, 0x00, }, err: io.ErrUnexpectedEOF, }, { desc: "1 short C-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x81, 0x00, 0x00, }, err: io.ErrUnexpectedEOF, }, { desc: "VLAN ID too large", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x81, 0x00, 0xff, 0xff, 0x00, 0x00, }, err: ErrInvalidVLAN, }, { desc: "no C-VLAN after S-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0xa8, 0x20, 0x65, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, }, err: ErrInvalidVLAN, }, { desc: "short C-VLAN after S-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0xa8, 0x20, 0x65, 0x81, 0x00, 0x00, 0x00, }, err: io.ErrUnexpectedEOF, }, { desc: "go-fuzz crasher: VLAN tag without enough bytes for trailing EtherType", b: []byte("190734863281\x81\x0032"), err: io.ErrUnexpectedEOF, }, { desc: "0 VLANs detected, but 1 may have been present", b: bytes.Repeat([]byte{0}, 56), f: &Frame{ Destination: net.HardwareAddr{0, 0, 0, 0, 0, 0}, Source: net.HardwareAddr{0, 0, 0, 0, 0, 0}, Payload: bytes.Repeat([]byte{0}, 42), }, }, { desc: "IPv4, no VLANs", b: append([]byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)...), f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, }, { desc: "IPv6, C-VLAN: (PRI 1, ID 101)", b: append([]byte{ 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0x81, 0x00, 0x20, 0x65, 0x86, 0xDD, }, bytes.Repeat([]byte{0}, 50)...), f: &Frame{ Destination: net.HardwareAddr{1, 0, 1, 0, 1, 0}, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeIPv6, Payload: bytes.Repeat([]byte{0}, 50), }, }, { desc: "ARP, S-VLAN: (PRI 0, DROP, ID 100), C-VLAN: (PRI 1, ID 101)", b: append([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 0, 1, 0, 1, 0x88, 0xa8, 0x10, 0x64, 0x81, 0x00, 0x20, 0x65, 0x08, 0x06, }, bytes.Repeat([]byte{0}, 50)...), f: &Frame{ Destination: Broadcast, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, ServiceVLAN: &VLAN{ DropEligible: true, ID: 100, }, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeARP, Payload: bytes.Repeat([]byte{0}, 50), }, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { f := new(Frame) if err := f.UnmarshalBinary(tt.b); err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.f, f; !reflect.DeepEqual(want, got) { t.Fatalf("unexpected Frame:\n- want: %v\n- got: %v", want, got) } }) } } func TestFrameUnmarshalFCS(t *testing.T) { var tests = []struct { desc string b []byte f *Frame err error }{ { desc: "too short for FCS", b: []byte{1, 2, 3}, err: io.ErrUnexpectedEOF, }, { desc: "invalid FCS", b: []byte{1, 2, 3, 4}, err: ErrInvalidFCS, }, { desc: "IPv4, no VLANs", b: append( append( []byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)..., ), []byte{159, 205, 24, 60}..., ), f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { f := new(Frame) if err := f.UnmarshalFCS(tt.b); err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.f, f; !reflect.DeepEqual(want, got) { t.Fatalf("unexpected Frame:\n- want: %v\n- got: %v", want, got) } }) } } // Benchmarks for Frame.MarshalBinary with varying VLAN tags and payloads func BenchmarkFrameMarshalBinary(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalBinary(b, f) } func BenchmarkFrameMarshalBinaryCVLAN(b *testing.B) { f := &Frame{ VLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalBinary(b, f) } func BenchmarkFrameMarshalBinarySVLANCVLAN(b *testing.B) { f := &Frame{ ServiceVLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, VLAN: &VLAN{ Priority: PriorityBestEffort, ID: 20, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalBinary(b, f) } func BenchmarkFrameMarshalBinaryJumboPayload(b *testing.B) { f := &Frame{ Payload: make([]byte, 8192), } benchmarkFrameMarshalBinary(b, f) } func benchmarkFrameMarshalBinary(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := f.MarshalBinary(); err != nil { b.Fatal(err) } } } // Benchmarks for Frame.MarshalFCS func BenchmarkFrameMarshalFCS(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalFCS(b, f) } func benchmarkFrameMarshalFCS(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := f.MarshalFCS(); err != nil { b.Fatal(err) } } } // Benchmarks for Frame.UnmarshalBinary with varying VLAN tags and payloads func BenchmarkFrameUnmarshalBinary(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalBinary(b, f) } func BenchmarkFrameUnmarshalBinaryCVLAN(b *testing.B) { f := &Frame{ VLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalBinary(b, f) } func BenchmarkFrameUnmarshalBinarySVLANCVLAN(b *testing.B) { f := &Frame{ ServiceVLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, VLAN: &VLAN{ Priority: PriorityBestEffort, ID: 20, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalBinary(b, f) } func BenchmarkFrameUnmarshalBinaryJumboPayload(b *testing.B) { f := &Frame{ Payload: make([]byte, 8192), } benchmarkFrameUnmarshalBinary(b, f) } func benchmarkFrameUnmarshalBinary(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} fb, err := f.MarshalBinary() if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if err := f.UnmarshalBinary(fb); err != nil { b.Fatal(err) } } } // Benchmarks for Frame.UnmarshalFCS func BenchmarkFrameUnmarshalFCS(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalFCS(b, f) } func benchmarkFrameUnmarshalFCS(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} fb, err := f.MarshalFCS() if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if err := f.UnmarshalFCS(fb); err != nil { b.Fatal(err) } } } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/fuzz.go000066400000000000000000000005261353774041100241010ustar00rootroot00000000000000// +build gofuzz package ethernet func Fuzz(data []byte) int { f := new(Frame) if err := f.UnmarshalBinary(data); err != nil { return 0 } if _, err := f.MarshalBinary(); err != nil { panic(err) } if err := f.UnmarshalFCS(data); err != nil { return 0 } if _, err := f.MarshalFCS(); err != nil { panic(err) } return 1 } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/string.go000066400000000000000000000015221353774041100244060ustar00rootroot00000000000000// Code generated by "stringer -output=string.go -type=EtherType"; DO NOT EDIT. package ethernet import "fmt" const ( _EtherType_name_0 = "EtherTypeIPv4" _EtherType_name_1 = "EtherTypeARP" _EtherType_name_2 = "EtherTypeVLAN" _EtherType_name_3 = "EtherTypeIPv6" _EtherType_name_4 = "EtherTypeServiceVLAN" ) var ( _EtherType_index_0 = [...]uint8{0, 13} _EtherType_index_1 = [...]uint8{0, 12} _EtherType_index_2 = [...]uint8{0, 13} _EtherType_index_3 = [...]uint8{0, 13} _EtherType_index_4 = [...]uint8{0, 20} ) func (i EtherType) String() string { switch { case i == 2048: return _EtherType_name_0 case i == 2054: return _EtherType_name_1 case i == 33024: return _EtherType_name_2 case i == 34525: return _EtherType_name_3 case i == 34984: return _EtherType_name_4 default: return fmt.Sprintf("EtherType(%d)", i) } } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/vlan.go000066400000000000000000000067461353774041100240550ustar00rootroot00000000000000package ethernet import ( "encoding/binary" "errors" "io" ) const ( // VLANNone is a special VLAN ID which indicates that no VLAN is being // used in a Frame. In this case, the VLAN's other fields may be used // to indicate a Frame's priority. VLANNone = 0x000 // VLANMax is a reserved VLAN ID which may indicate a wildcard in some // management systems, but may not be configured or transmitted in a // VLAN tag. VLANMax = 0xfff ) var ( // ErrInvalidVLAN is returned when a VLAN tag is invalid due to one of the // following reasons: // - Priority of greater than 7 is detected // - ID of greater than 4094 (0xffe) is detected // - A customer VLAN does not follow a service VLAN (when using Q-in-Q) ErrInvalidVLAN = errors.New("invalid VLAN") ) // Priority is an IEEE P802.1p priority level. Priority can be any value from // 0 to 7. // // It is important to note that priority 1 (PriorityBackground) actually has // a lower priority than 0 (PriorityBestEffort). All other Priority constants // indicate higher priority as the integer values increase. type Priority uint8 // IEEE P802.1p recommended priority levels. Note that PriorityBackground has // a lower priority than PriorityBestEffort. const ( PriorityBackground Priority = 1 PriorityBestEffort Priority = 0 PriorityExcellentEffort Priority = 2 PriorityCriticalApplications Priority = 3 PriorityVideo Priority = 4 PriorityVoice Priority = 5 PriorityInternetworkControl Priority = 6 PriorityNetworkControl Priority = 7 ) // A VLAN is an IEEE 802.1Q Virtual LAN (VLAN) tag. A VLAN contains // information regarding traffic priority and a VLAN identifier for // a given Frame. type VLAN struct { // Priority specifies a IEEE P802.1p priority level. Priority can be any // value from 0 to 7. Priority Priority // DropEligible indicates if a Frame is eligible to be dropped in the // presence of network congestion. DropEligible bool // ID specifies the VLAN ID for a Frame. ID can be any value from 0 to // 4094 (0x000 to 0xffe), allowing up to 4094 VLANs. // // If ID is 0 (0x000, VLANNone), no VLAN is specified, and the other fields // simply indicate a Frame's priority. ID uint16 } // MarshalBinary allocates a byte slice and marshals a VLAN into binary form. func (v *VLAN) MarshalBinary() ([]byte, error) { b := make([]byte, 2) _, err := v.read(b) return b, err } // read reads data from a VLAN into b. read is used to marshal a VLAN into // binary form, but does not allocate on its own. func (v *VLAN) read(b []byte) (int, error) { // Check for VLAN priority in valid range if v.Priority > PriorityNetworkControl { return 0, ErrInvalidVLAN } // Check for VLAN ID in valid range if v.ID >= VLANMax { return 0, ErrInvalidVLAN } // 3 bits: priority ub := uint16(v.Priority) << 13 // 1 bit: drop eligible var drop uint16 if v.DropEligible { drop = 1 } ub |= drop << 12 // 12 bits: VLAN ID ub |= v.ID binary.BigEndian.PutUint16(b, ub) return 2, nil } // UnmarshalBinary unmarshals a byte slice into a VLAN. func (v *VLAN) UnmarshalBinary(b []byte) error { // VLAN tag is always 2 bytes if len(b) != 2 { return io.ErrUnexpectedEOF } // 3 bits: priority // 1 bit : drop eligible // 12 bits: VLAN ID ub := binary.BigEndian.Uint16(b[0:2]) v.Priority = Priority(uint8(ub >> 13)) v.DropEligible = ub&0x1000 != 0 v.ID = ub & 0x0fff // Check for VLAN ID in valid range if v.ID >= VLANMax { return ErrInvalidVLAN } return nil } vip-manager-0.6/vendor/github.com/mdlayher/ethernet/vlan_test.go000066400000000000000000000053731353774041100251070ustar00rootroot00000000000000package ethernet import ( "bytes" "io" "reflect" "testing" ) func TestVLANMarshalBinary(t *testing.T) { var tests = []struct { desc string v *VLAN b []byte err error }{ { desc: "VLAN priority too large", v: &VLAN{ Priority: 8, }, err: ErrInvalidVLAN, }, { desc: "VLAN ID too large", v: &VLAN{ ID: 4095, }, err: ErrInvalidVLAN, }, { desc: "empty VLAN", v: &VLAN{}, b: []byte{0x00, 0x00}, }, { desc: "VLAN: PRI 1, ID 101", v: &VLAN{ Priority: 1, ID: 101, }, b: []byte{0x20, 0x65}, }, { desc: "VLANs: PRI 0, DROP, ID 100", v: &VLAN{ DropEligible: true, ID: 100, }, b: []byte{0x10, 0x64}, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { b, err := tt.v.MarshalBinary() if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.b, b; !bytes.Equal(want, got) { t.Fatalf("unexpected VLAN bytes:\n- want: %v\n- got: %v", want, got) } }) } } func TestVLANUnmarshalBinary(t *testing.T) { var tests = []struct { desc string b []byte v *VLAN err error }{ { desc: "nil buffer", err: io.ErrUnexpectedEOF, }, { desc: "short buffer", b: []byte{0}, err: io.ErrUnexpectedEOF, }, { desc: "VLAN ID too large", b: []byte{0xff, 0xff}, err: ErrInvalidVLAN, }, { desc: "VLAN: PRI 1, ID 101", b: []byte{0x20, 0x65}, v: &VLAN{ Priority: 1, ID: 101, }, }, { desc: "VLAN: PRI 0, DROP, ID 100", b: []byte{0x10, 0x64}, v: &VLAN{ DropEligible: true, ID: 100, }, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { v := new(VLAN) if err := v.UnmarshalBinary(tt.b); err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.v, v; !reflect.DeepEqual(want, got) { t.Fatalf("unexpected VLAN:\n- want: %v\n- got: %v", want, got) } }) } } // Benchmarks for VLAN.MarshalBinary func BenchmarkVLANMarshalBinary(b *testing.B) { v := &VLAN{ Priority: PriorityBackground, ID: 10, } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := v.MarshalBinary(); err != nil { b.Fatal(err) } } } // Benchmarks for VLAN.UnmarshalBinary func BenchmarkVLANUnmarshalBinary(b *testing.B) { v := &VLAN{ Priority: PriorityBestEffort, ID: 20, } vb, err := v.MarshalBinary() if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if err := v.UnmarshalBinary(vb); err != nil { b.Fatal(err) } } } vip-manager-0.6/vendor/github.com/mdlayher/raw/000077500000000000000000000000001353774041100215245ustar00rootroot00000000000000vip-manager-0.6/vendor/github.com/mdlayher/raw/.travis.yml000066400000000000000000000005511353774041100236360ustar00rootroot00000000000000language: go go: - 1.x os: - linux - osx before_install: - go get github.com/golang/lint/golint - go get honnef.co/go/tools/cmd/staticcheck - go get -d ./... script: - go build -tags=gofuzz ./... - go vet ./... - staticcheck -ignore 'github.com/mdlayher/raw/raw_bsd.go:SA1019' ./... - golint -set_exit_status ./... - go test -v -race ./...vip-manager-0.6/vendor/github.com/mdlayher/raw/LICENSE.md000066400000000000000000000020701353774041100231270ustar00rootroot00000000000000MIT License =========== Copyright (C) 2015 Matt Layher 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. vip-manager-0.6/vendor/github.com/mdlayher/raw/README.md000066400000000000000000000016141353774041100230050ustar00rootroot00000000000000raw [![Build Status](https://travis-ci.org/mdlayher/raw.svg?branch=master)](https://travis-ci.org/mdlayher/raw) [![GoDoc](https://godoc.org/github.com/mdlayher/raw?status.svg)](https://godoc.org/github.com/mdlayher/raw) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/raw)](https://goreportcard.com/report/github.com/mdlayher/raw) === Package `raw` enables reading and writing data at the device driver level for a network interface. MIT Licensed. For more information about using raw sockets with Ethernet frames in Go, check out my blog post: [Network Protocol Breakdown: Ethernet and Go](https://medium.com/@mdlayher/network-protocol-breakdown-ethernet-and-go-de985d726cc1). Portions of this code are taken from the Go standard library. The Go standard library is Copyright (c) 2012 The Go Authors. All rights reserved. The Go license can be found at https://golang.org/LICENSE.vip-manager-0.6/vendor/github.com/mdlayher/raw/raw.go000066400000000000000000000102011353774041100226360ustar00rootroot00000000000000// Package raw enables reading and writing data at the device driver level for // a network interface. package raw import ( "errors" "net" "time" "golang.org/x/net/bpf" ) const ( // ProtocolAoE specifies the ATA over Ethernet protocol (AoEr11). ProtocolAoE Protocol = 0x88a2 // ProtocolARP specifies the Address Resolution Protocol (RFC 826). ProtocolARP Protocol = 0x0806 // ProtocolWoL specifies the Wake-on-LAN protocol. ProtocolWoL Protocol = 0x0842 ) var ( // ErrNotImplemented is returned when certain functionality is not yet // implemented for the host operating system. ErrNotImplemented = errors.New("raw: not implemented") ) var _ net.Addr = &Addr{} // Addr is a network address which can be used to contact other machines, using // their hardware addresses. type Addr struct { HardwareAddr net.HardwareAddr } // Network returns the address's network name, "raw". func (a *Addr) Network() string { return "raw" } // String returns the address's hardware address. func (a *Addr) String() string { return a.HardwareAddr.String() } var _ net.PacketConn = &Conn{} // Conn is an implementation of the net.PacketConn interface which can send // and receive data at the network interface device driver level. type Conn struct { // packetConn is the operating system-specific implementation of // a raw connection. p *packetConn } // ReadFrom implements the net.PacketConn ReadFrom method. func (c *Conn) ReadFrom(b []byte) (int, net.Addr, error) { return c.p.ReadFrom(b) } // WriteTo implements the net.PacketConn WriteTo method. func (c *Conn) WriteTo(b []byte, addr net.Addr) (int, error) { return c.p.WriteTo(b, addr) } // Close closes the connection. func (c *Conn) Close() error { return c.p.Close() } // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { return c.p.LocalAddr() } // SetDeadline implements the net.PacketConn SetDeadline method. func (c *Conn) SetDeadline(t time.Time) error { return c.p.SetDeadline(t) } // SetReadDeadline implements the net.PacketConn SetReadDeadline method. func (c *Conn) SetReadDeadline(t time.Time) error { return c.p.SetReadDeadline(t) } // SetWriteDeadline implements the net.PacketConn SetWriteDeadline method. func (c *Conn) SetWriteDeadline(t time.Time) error { return c.p.SetWriteDeadline(t) } var _ bpf.Setter = &Conn{} // SetBPF attaches an assembled BPF program to the connection. func (c *Conn) SetBPF(filter []bpf.RawInstruction) error { return c.p.SetBPF(filter) } // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it // to receive traffic that is not addressed to the interface. func (c *Conn) SetPromiscuous(b bool) error { return c.p.SetPromiscuous(b) } // A Protocol is a network protocol constant which identifies the type of // traffic a raw socket should send and receive. type Protocol uint16 // ListenPacket creates a net.PacketConn which can be used to send and receive // data at the network interface device driver level. // // ifi specifies the network interface which will be used to send and receive // data. proto specifies the protocol which should be captured and // transmitted. proto, if needed, is automatically converted to network byte // order (big endian), akin to the htons() function in C. func ListenPacket(ifi *net.Interface, proto Protocol) (*Conn, error) { p, err := listenPacket(ifi, proto) if err != nil { return nil, err } return &Conn{ p: p, }, nil } // htons converts a short (uint16) from host-to-network byte order. // Thanks to mikioh for this neat trick: // https://github.com/mikioh/-stdyng/blob/master/afpacket.go func htons(i uint16) uint16 { return (i<<8)&0xff00 | i>>8 } // Copyright (c) 2012 The Go Authors. All rights reserved. // Source code in this file is based on src/net/interface_linux.go, // from the Go standard library. The Go license can be found here: // https://golang.org/LICENSE. // Taken from: // https://github.com/golang/go/blob/master/src/net/net.go#L417-L421. type timeoutError struct{} func (e *timeoutError) Error() string { return "i/o timeout" } func (e *timeoutError) Timeout() bool { return true } func (e *timeoutError) Temporary() bool { return true } vip-manager-0.6/vendor/github.com/mdlayher/raw/raw_bsd.go000066400000000000000000000202001353774041100234660ustar00rootroot00000000000000// +build darwin dragonfly freebsd netbsd openbsd package raw import ( "errors" "fmt" "net" "os" "runtime" "syscall" "time" "unsafe" "golang.org/x/net/bpf" ) const ( // bpfDIn tells BPF to pass through only incoming packets, so we do not // receive the packets we send using BPF. bpfDIn = 0 // osFreeBSD is the GOOS name for FreeBSD. osFreeBSD = "freebsd" ) // bpfLen returns the length of the BPF header prepended to each incoming ethernet // frame. FreeBSD uses a slightly modified header from other BSD variants. func bpfLen() int { // Majority of BSD family systems use the bpf_hdr struct, but FreeBSD // has replaced this with bpf_xhdr, which is longer. const ( bpfHeaderLen = 18 bpfXHeaderLen = 26 ) if runtime.GOOS == osFreeBSD { return bpfXHeaderLen } return bpfHeaderLen } var ( // Must implement net.PacketConn at compile-time. _ net.PacketConn = &packetConn{} ) // packetConn is the Linux-specific implementation of net.PacketConn for this // package. type packetConn struct { proto Protocol ifi *net.Interface f *os.File fd int buflen int } // listenPacket creates a net.PacketConn which can be used to send and receive // data at the device driver level. // // ifi specifies the network interface which will be used to send and receive // data. proto specifies the protocol which should be captured and // transmitted. func listenPacket(ifi *net.Interface, proto Protocol) (*packetConn, error) { var f *os.File var err error // Try to find an available BPF device for i := 0; i <= 10; i++ { bpfPath := fmt.Sprintf("/dev/bpf%d", i) f, err = os.OpenFile(bpfPath, os.O_RDWR, 0666) if err == nil { // Found a usable device break } // Device is busy, try the next one if perr, ok := err.(*os.PathError); ok { if perr.Err.(syscall.Errno) == syscall.EBUSY { continue } } return nil, err } if f == nil { return nil, errors.New("unable to open BPF device") } fd := int(f.Fd()) if fd == -1 { return nil, errors.New("unable to open BPF device") } // Configure BPF device to send and receive data buflen, err := configureBPF(fd, ifi, proto) if err != nil { return nil, err } return &packetConn{ proto: proto, ifi: ifi, f: f, fd: fd, buflen: buflen, }, nil } // ReadFrom implements the net.PacketConn.ReadFrom method. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { // Attempt to receive on socket buf := make([]byte, p.buflen) n, err := syscall.Read(p.fd, buf) if err != nil { // Return other errors return n, nil, err } // TODO(mdlayher): consider parsing BPF header if it proves useful. // BPF header length depends on the platform this code is running on bpfl := bpfLen() // Retrieve source MAC address of ethernet header mac := make(net.HardwareAddr, 6) copy(mac, buf[bpfl+6:bpfl+12]) // Skip past BPF header to retrieve ethernet frame out := copy(b, buf[bpfl:bpfl+n]) return out, &Addr{ HardwareAddr: mac, }, nil } // WriteTo implements the net.PacketConn.WriteTo method. func (p *packetConn) WriteTo(b []byte, _ net.Addr) (int, error) { return syscall.Write(p.fd, b) } // Close closes the connection. func (p *packetConn) Close() error { return p.f.Close() } // LocalAddr returns the local network address. func (p *packetConn) LocalAddr() net.Addr { return &Addr{ HardwareAddr: p.ifi.HardwareAddr, } } // SetDeadline implements the net.PacketConn.SetDeadline method. func (p *packetConn) SetDeadline(t time.Time) error { return ErrNotImplemented } // SetReadDeadline implements the net.PacketConn.SetReadDeadline method. func (p *packetConn) SetReadDeadline(t time.Time) error { return ErrNotImplemented } // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method. func (p *packetConn) SetWriteDeadline(t time.Time) error { return ErrNotImplemented } // SetBPF attaches an assembled BPF program to a raw net.PacketConn. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { // Base filter filters traffic based on EtherType base, err := bpf.Assemble(baseFilter(p.proto)) if err != nil { return err } // Append user filter to base filter, translate to raw format, // and apply to BPF device return syscall.SetBpf(p.fd, assembleBpfInsn(append(base, filter...))) } // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it // to receive traffic that is not addressed to the interface. func (p *packetConn) SetPromiscuous(b bool) error { m := 1 if !b { m = 0 } return syscall.SetBpfPromisc(p.fd, m) } // configureBPF configures a BPF device with the specified file descriptor to // use the specified network and interface and protocol. func configureBPF(fd int, ifi *net.Interface, proto Protocol) (int, error) { // Use specified interface with BPF device if err := syscall.SetBpfInterface(fd, ifi.Name); err != nil { return 0, err } // Inform BPF to send us its data immediately if err := syscall.SetBpfImmediate(fd, 1); err != nil { return 0, err } // Check buffer size of BPF device buflen, err := syscall.BpfBuflen(fd) if err != nil { return 0, err } // Do not automatically complete source address in ethernet headers if err := syscall.SetBpfHeadercmpl(fd, 1); err != nil { return 0, err } // Only retrieve incoming traffic using BPF device if err := setBPFDirection(fd, bpfDIn); err != nil { return 0, err } // Build and apply base BPF filter which checks for correct EtherType // on incoming packets prog, err := bpf.Assemble(baseInterfaceFilter(proto, ifi.MTU)) if err != nil { return 0, err } if err := syscall.SetBpf(fd, assembleBpfInsn(prog)); err != nil { return 0, err } // Flush any packets currently in the BPF device's buffer if err := syscall.FlushBpf(fd); err != nil { return 0, err } return buflen, nil } // setBPFDirection enables filtering traffic traveling in a specific direction // using BPF, so that traffic sent by this package is not captured when reading // using this package. func setBPFDirection(fd int, direction int) error { _, _, err := syscall.Syscall( syscall.SYS_IOCTL, uintptr(fd), // Even though BIOCSDIRECTION is preferred on FreeBSD, BIOCSSEESENT continues // to work, and is required for other BSD platforms syscall.BIOCSSEESENT, uintptr(unsafe.Pointer(&direction)), ) if err != 0 { return syscall.Errno(err) } return nil } // assembleBpfInsn assembles a slice of bpf.RawInstructions to the format required by // package syscall. func assembleBpfInsn(filter []bpf.RawInstruction) []syscall.BpfInsn { // Copy each bpf.RawInstruction into syscall.BpfInsn. If needed, // the structures have the same memory layout and could probably be // unsafely cast to each other for speed. insns := make([]syscall.BpfInsn, 0, len(filter)) for _, ins := range filter { insns = append(insns, syscall.BpfInsn{ Code: ins.Op, Jt: ins.Jt, Jf: ins.Jf, K: ins.K, }) } return insns } // baseInterfaceFilter creates a base BPF filter which filters traffic based // on its EtherType and returns up to "mtu" bytes of data for processing. func baseInterfaceFilter(proto Protocol, mtu int) []bpf.Instruction { return append( // Filter traffic based on EtherType baseFilter(proto), // Accept the packet bytes up to the interface's MTU bpf.RetConstant{ Val: uint32(mtu), }, ) } // baseFilter creates a base BPF filter which filters traffic based on its // EtherType. baseFilter can be prepended to other filters to handle common // filtering tasks. func baseFilter(proto Protocol) []bpf.Instruction { // Offset | Length | Comment // ------------------------- // 00 | 06 | Ethernet destination MAC address // 06 | 06 | Ethernet source MAC address // 12 | 02 | Ethernet EtherType const ( etherTypeOffset = 12 etherTypeLength = 2 ) return []bpf.Instruction{ // Load EtherType value from Ethernet header bpf.LoadAbsolute{ Off: etherTypeOffset, Size: etherTypeLength, }, // If EtherType is equal to the protocol we are using, jump to instructions // added outside of this function. bpf.JumpIf{ Cond: bpf.JumpEqual, Val: uint32(proto), SkipTrue: 1, }, // EtherType does not match our protocol bpf.RetConstant{ Val: 0, }, } } vip-manager-0.6/vendor/github.com/mdlayher/raw/raw_linux.go000066400000000000000000000217631353774041100240740ustar00rootroot00000000000000// +build linux package raw import ( "net" "os" "sync" "syscall" "time" "unsafe" "golang.org/x/net/bpf" "golang.org/x/net/context" "golang.org/x/sys/unix" ) var ( // Must implement net.PacketConn at compile-time. _ net.PacketConn = &packetConn{} ) // packetConn is the Linux-specific implementation of net.PacketConn for this // package. type packetConn struct { ifi *net.Interface s socket // Sleep function implementation sleeper sleeper // Timeouts set via Set{Read,}Deadline, guarded by mutex timeoutMu sync.RWMutex nonblocking bool rtimeout time.Time } // socket is an interface which enables swapping out socket syscalls for // testing. type socket interface { Bind(syscall.Sockaddr) error Close() error FD() int Recvfrom([]byte, int) (int, syscall.Sockaddr, error) Sendto([]byte, int, syscall.Sockaddr) error SetNonblock(bool) error SetSockopt(level, name int, v unsafe.Pointer, l uint32) error } // sleeper is an interface which enables swapping out an actual time.Sleep // call for testing. type sleeper interface { Sleep(time.Duration) } // listenPacket creates a net.PacketConn which can be used to send and receive // data at the device driver level. // // ifi specifies the network interface which will be used to send and receive // data. proto specifies the protocol which should be captured and // transmitted. proto is automatically converted to network byte // order (big endian), akin to the htons() function in C. func listenPacket(ifi *net.Interface, proto Protocol) (*packetConn, error) { // Convert proto to big endian pbe := htons(uint16(proto)) // Open a packet socket using specified socket and protocol types sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(pbe)) if err != nil { return nil, err } // Wrap raw socket in socket interface, use actual time package sleeper return newPacketConn( ifi, &sysSocket{ fd: sock, }, pbe, &timeSleeper{}, ) } // newPacketConn creates a net.PacketConn using the specified network // interface, wrapped socket, big endian protocol number, and Sleep // implementation used for read/write retries. // // It is the entry point for tests in this package. func newPacketConn(ifi *net.Interface, s socket, pbe uint16, sleeper sleeper) (*packetConn, error) { // Bind the packet socket to the interface specified by ifi // packet(7): // Only the sll_protocol and the sll_ifindex address fields are used for // purposes of binding. err := s.Bind(&syscall.SockaddrLinklayer{ Protocol: pbe, Ifindex: ifi.Index, }) return &packetConn{ ifi: ifi, s: s, sleeper: sleeper, }, err } // ReadFrom implements the net.PacketConn.ReadFrom method. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { // Set up deadline context if needed, if a read timeout is set ctx, cancel := context.TODO(), func() {} p.timeoutMu.RLock() if p.rtimeout.After(time.Now()) { ctx, cancel = context.WithDeadline(context.Background(), p.rtimeout) } p.timeoutMu.RUnlock() // Information returned by syscall.Recvfrom var n int var addr syscall.Sockaddr var err error for { // Continue looping, or if deadline is set and has expired, return // an error select { case <-ctx.Done(): // We only know how to handle deadline exceeded, so return any // other errors for the caller to deal with if err := ctx.Err(); err != context.DeadlineExceeded { return n, nil, err } // Return standard net.OpError so caller can detect timeouts and retry return n, nil, &net.OpError{ Op: "read", Net: "raw", Addr: nil, Err: &timeoutError{}, } default: // Not timed out, keep trying } // Attempt to receive on socket n, addr, err = p.s.Recvfrom(b, 0) if err != nil { n = 0 // EAGAIN is returned when no data is available for non-blocking // I/O, so keep trying after a short delay if err == syscall.EAGAIN { p.sleeper.Sleep(2 * time.Millisecond) continue } // Return other errors return n, nil, err } // Got data, cancel the deadline cancel() break } // Retrieve hardware address and other information from addr sa, ok := addr.(*syscall.SockaddrLinklayer) if !ok || sa.Halen < 6 { return n, nil, syscall.EINVAL } // Use length specified to convert byte array into a hardware address slice mac := make(net.HardwareAddr, sa.Halen) copy(mac, sa.Addr[:]) // packet(7): // sll_hatype and sll_pkttype are set on received packets for your // information. // TODO(mdlayher): determine if similar fields exist and are useful on // non-Linux platforms return n, &Addr{ HardwareAddr: mac, }, nil } // WriteTo implements the net.PacketConn.WriteTo method. func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { // Ensure correct Addr type a, ok := addr.(*Addr) if !ok || len(a.HardwareAddr) < 6 { return 0, syscall.EINVAL } // Convert hardware address back to byte array form var baddr [8]byte copy(baddr[:], a.HardwareAddr) // Send message on socket to the specified hardware address from addr // packet(7): // When you send packets it is enough to specify sll_family, sll_addr, // sll_halen, sll_ifindex. The other fields should be 0. // In this case, sll_family is taken care of automatically by syscall err := p.s.Sendto(b, 0, &syscall.SockaddrLinklayer{ Ifindex: p.ifi.Index, Halen: uint8(len(a.HardwareAddr)), Addr: baddr, }) return len(b), err } // Close closes the connection. func (p *packetConn) Close() error { return p.s.Close() } // LocalAddr returns the local network address. func (p *packetConn) LocalAddr() net.Addr { return &Addr{ HardwareAddr: p.ifi.HardwareAddr, } } // TODO(mdlayher): it is unfortunate that we have to implement deadlines using // a context, but it appears that there may not be a better solution until // Go 1.6 or later. See here: https://github.com/golang/go/issues/10565. // SetDeadline implements the net.PacketConn.SetDeadline method. func (p *packetConn) SetDeadline(t time.Time) error { return p.SetReadDeadline(t) } // SetReadDeadline implements the net.PacketConn.SetReadDeadline method. func (p *packetConn) SetReadDeadline(t time.Time) error { p.timeoutMu.Lock() // Set nonblocking I/O so we can time out reads and writes // // This is set only if timeouts are used, because a server probably // does not want timeouts by default, and a client can request them // itself if needed. var err error // If already nonblocking and the zero-value for t is entered, disable // nonblocking mode if p.nonblocking && t.IsZero() { err = p.s.SetNonblock(false) p.nonblocking = false } else if !p.nonblocking && t.After(time.Now()) { // If not nonblocking and t is after current time, enable nonblocking // mode err = p.s.SetNonblock(true) p.nonblocking = true } p.rtimeout = t p.timeoutMu.Unlock() return err } // SetWriteDeadline implements the net.PacketConn.SetWriteDeadline method. func (p *packetConn) SetWriteDeadline(t time.Time) error { return nil } // SetBPF attaches an assembled BPF program to a raw net.PacketConn. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { prog := syscall.SockFprog{ Len: uint16(len(filter)), Filter: (*syscall.SockFilter)(unsafe.Pointer(&filter[0])), } err := p.s.SetSockopt( syscall.SOL_SOCKET, syscall.SO_ATTACH_FILTER, unsafe.Pointer(&prog), uint32(unsafe.Sizeof(prog)), ) if err != nil { return os.NewSyscallError("setsockopt", err) } return nil } // SetPromiscuous enables or disables promiscuous mode on the interface, allowing it // to receive traffic that is not addressed to the interface. func (p *packetConn) SetPromiscuous(b bool) error { mreq := unix.PacketMreq{ Ifindex: int32(p.ifi.Index), Type: unix.PACKET_MR_PROMISC, } membership := unix.PACKET_ADD_MEMBERSHIP if !b { membership = unix.PACKET_DROP_MEMBERSHIP } return p.s.SetSockopt(unix.SOL_PACKET, membership, unsafe.Pointer(&mreq), unix.SizeofPacketMreq) } // sysSocket is the default socket implementation. It makes use of // Linux-specific system calls to handle raw socket functionality. type sysSocket struct { fd int } // Method implementations simply invoke the syscall of the same name, but pass // the file descriptor stored in the sysSocket as the socket to use. func (s *sysSocket) Bind(sa syscall.Sockaddr) error { return syscall.Bind(s.fd, sa) } func (s *sysSocket) Close() error { return syscall.Close(s.fd) } func (s *sysSocket) FD() int { return s.fd } func (s *sysSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) { return syscall.Recvfrom(s.fd, p, flags) } func (s *sysSocket) Sendto(p []byte, flags int, to syscall.Sockaddr) error { return syscall.Sendto(s.fd, p, flags, to) } func (s *sysSocket) SetNonblock(nonblocking bool) error { return syscall.SetNonblock(s.fd, nonblocking) } func (s *sysSocket) SetSockopt(level, name int, v unsafe.Pointer, l uint32) error { return setsockopt(s.fd, level, name, v, l) } // timeSleeper sleeps using time.Sleep. type timeSleeper struct{} func (timeSleeper) Sleep(d time.Duration) { time.Sleep(d) } vip-manager-0.6/vendor/github.com/mdlayher/raw/raw_linux_test.go000066400000000000000000000304541353774041100251300ustar00rootroot00000000000000// +build linux package raw import ( "bytes" "errors" "net" "sync/atomic" "syscall" "testing" "time" "unsafe" "golang.org/x/net/bpf" ) // Test to ensure that socket is bound with correct sockaddr_ll information type bindSocket struct { bind syscall.Sockaddr noopSocket } func (s *bindSocket) Bind(sa syscall.Sockaddr) error { s.bind = sa return nil } func Test_newPacketConnBind(t *testing.T) { s := &bindSocket{} ifIndex := 1 protocol := uint16(1) _, err := newPacketConn( &net.Interface{ Index: ifIndex, }, s, protocol, &testSleeper{}, ) if err != nil { t.Fatal(err) } sall, ok := s.bind.(*syscall.SockaddrLinklayer) if !ok { t.Fatalf("bind sockaddr has incorrect type: %T", s.bind) } if want, got := ifIndex, sall.Ifindex; want != got { t.Fatalf("unexpected network interface index:\n- want: %v\n- got: %v", want, got) } if want, got := protocol, sall.Protocol; want != got { t.Fatalf("unexpected protocol:\n- want: %v\n- got: %v", want, got) } } // Test for errors which occur after several retries while attempting to // recvfrom on a socket. type errRetryNRecvfromSocket struct { n int try int err error noopSocket } func (s *errRetryNRecvfromSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) { if s.try == s.n { return 0, nil, s.err } s.try++ return 0, nil, syscall.EAGAIN } func Test_packetConnReadFromRecvfromRetryNError(t *testing.T) { fooErr := errors.New("foo") ts := &testSleeper{} const n = 5 p, err := newPacketConn( &net.Interface{}, &errRetryNRecvfromSocket{ n: n, err: fooErr, }, 0, ts, ) if err != nil { t.Fatal(err) } _, _, err = p.ReadFrom(nil) if want, got := fooErr, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } if want, got := n*(2*time.Millisecond), time.Duration(ts.slept); want != got { t.Fatalf("unexpected mock sleep time:\n- want: %v\n- got: %v", want, got) } } // Test for incorrect sockaddr type after recvfrom on a socket. type addrRecvfromSocket struct { addr syscall.Sockaddr noopSocket } func (s *addrRecvfromSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) { return 0, s.addr, nil } func Test_packetConnReadFromRecvfromInvalidSockaddr(t *testing.T) { p, err := newPacketConn( &net.Interface{}, &addrRecvfromSocket{ addr: &syscall.SockaddrInet4{}, }, 0, &testSleeper{}, ) if err != nil { t.Fatal(err) } _, _, err = p.ReadFrom(nil) if want, got := syscall.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for malformed hardware address after recvfrom on a socket func Test_packetConnReadFromRecvfromInvalidHardwareAddr(t *testing.T) { p, err := newPacketConn( &net.Interface{}, &addrRecvfromSocket{ addr: &syscall.SockaddrLinklayer{ Halen: 5, }, }, 0, &testSleeper{}, ) if err != nil { t.Fatal(err) } _, _, err = p.ReadFrom(nil) if want, got := syscall.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for a correct ReadFrom with data and address. type recvfromSocket struct { p []byte flags int addr syscall.Sockaddr noopSocket } func (s *recvfromSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) { copy(p, s.p) s.flags = flags return len(s.p), s.addr, nil } func Test_packetConnReadFromRecvfromOK(t *testing.T) { const wantN = 4 data := []byte{0, 1, 2, 3} deadbeefHW := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} s := &recvfromSocket{ p: data, addr: &syscall.SockaddrLinklayer{ Halen: 6, Addr: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0x00, 0x00}, }, } p, err := newPacketConn( &net.Interface{}, s, 0, &testSleeper{}, ) if err != nil { t.Fatal(err) } buf := make([]byte, 8) n, addr, err := p.ReadFrom(buf) if err != nil { t.Fatal(err) } if want, got := 0, s.flags; want != got { t.Fatalf("unexpected flags:\n- want: %v\n- got: %v", want, got) } raddr, ok := addr.(*Addr) if !ok { t.Fatalf("read sockaddr has incorrect type: %T", addr) } if want, got := deadbeefHW, raddr.HardwareAddr; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address:\n- want: %v\n- got: %v", want, got) } if want, got := wantN, n; want != got { t.Fatalf("unexpected data length:\n- want: %v\n- got: %v", want, got) } if want, got := data, buf[:n]; !bytes.Equal(want, got) { t.Fatalf("unexpected data:\n- want: %v\n- got: %v", want, got) } } // Test for incorrect sockaddr type for WriteTo. func Test_packetConnWriteToInvalidSockaddr(t *testing.T) { _, err := (&packetConn{}).WriteTo(nil, &net.IPAddr{}) if want, got := syscall.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for malformed hardware address with WriteTo. func Test_packetConnWriteToInvalidHardwareAddr(t *testing.T) { _, err := (&packetConn{}).WriteTo(nil, &Addr{ HardwareAddr: net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde}, }) if want, got := syscall.EINVAL, err; want != got { t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) } } // Test for a correct WriteTo with data and address. type sendtoSocket struct { p []byte flags int addr syscall.Sockaddr noopSocket } func (s *sendtoSocket) Sendto(p []byte, flags int, to syscall.Sockaddr) error { copy(s.p, p) s.flags = flags s.addr = to return nil } func Test_packetConnWriteToSendtoOK(t *testing.T) { const wantN = 4 data := []byte{0, 1, 2, 3} deadbeefHW := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} s := &sendtoSocket{ p: make([]byte, wantN), } p, err := newPacketConn( &net.Interface{}, s, 0, &testSleeper{}, ) if err != nil { t.Fatal(err) } n, err := p.WriteTo(data, &Addr{ HardwareAddr: deadbeefHW, }) if err != nil { t.Fatal(err) } if want, got := 0, s.flags; want != got { t.Fatalf("unexpected flags:\n- want: %v\n- got: %v", want, got) } if want, got := wantN, n; want != got { t.Fatalf("unexpected data length:\n- want: %v\n- got: %v", want, got) } if want, got := data, s.p; !bytes.Equal(want, got) { t.Fatalf("unexpected data:\n- want: %v\n- got: %v", want, got) } sall, ok := s.addr.(*syscall.SockaddrLinklayer) if !ok { t.Fatalf("write sockaddr has incorrect type: %T", s.addr) } if want, got := deadbeefHW, sall.Addr[:][:sall.Halen]; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address:\n- want: %v\n- got: %v", want, got) } } // Test that socket close functions as intended. type captureCloseSocket struct { closed bool noopSocket } func (s *captureCloseSocket) Close() error { s.closed = true return nil } func Test_packetConnClose(t *testing.T) { s := &captureCloseSocket{} p := &packetConn{ s: s, } if err := p.Close(); err != nil { t.Fatal(err) } if !s.closed { t.Fatalf("socket should be closed, but is not") } } // Test that LocalAddr returns the hardware address of the network interface // which is being used by the socket. func Test_packetConnLocalAddr(t *testing.T) { deadbeefHW := net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} p := &packetConn{ ifi: &net.Interface{ HardwareAddr: deadbeefHW, }, } if want, got := deadbeefHW, p.LocalAddr().(*Addr).HardwareAddr; !bytes.Equal(want, got) { t.Fatalf("unexpected hardware address:\n- want: %v\n- got: %v", want, got) } } // Test to ensure that nonblocking mode appropriately toggles depending on // input, and that it remains toggled correctly for various inputs. type setNonblockSocket struct { nonblocking bool triggered bool noopSocket } func (s *setNonblockSocket) SetNonblock(nonblocking bool) error { s.nonblocking = nonblocking s.triggered = true return nil } func Test_packetConnSetNonblock(t *testing.T) { s := &setNonblockSocket{} p, err := newPacketConn( &net.Interface{}, s, 0, &testSleeper{}, ) if err != nil { t.Fatal(err) } // Test 1: socket should remain blocking due to zero time, SetNonblock // should never have been triggered if err := p.SetDeadline(time.Time{}); err != nil { t.Fatal(err) } if want, got := false, s.triggered; want != got { t.Fatalf("unexpected triggered boolean:\n- want: %v\n- got: %v", want, got) } if want, got := false, s.nonblocking; want != got { t.Fatalf("unexpected nonblocking boolean:\n- want: %v\n- got: %v", want, got) } // Reset trigger s.triggered = false // Test 2: socket should become nonblocking due to time after now if err := p.SetDeadline(time.Now().Add(10 * time.Second)); err != nil { t.Fatal(err) } if want, got := true, s.triggered; want != got { t.Fatalf("unexpected triggered boolean:\n- want: %v\n- got: %v", want, got) } if want, got := true, s.nonblocking; want != got { t.Fatalf("unexpected nonblocking boolean:\n- want: %v\n- got: %v", want, got) } // Reset trigger s.triggered = false // Test 3: socket should remain nonblocking due to time after now, but not // trigger the system call again if err := p.SetDeadline(time.Now().Add(10 * time.Second)); err != nil { t.Fatal(err) } if want, got := false, s.triggered; want != got { t.Fatalf("unexpected triggered boolean:\n- want: %v\n- got: %v", want, got) } if want, got := true, s.nonblocking; want != got { t.Fatalf("unexpected nonblocking boolean:\n- want: %v\n- got: %v", want, got) } // Reset trigger s.triggered = false // Test 4: socket should become blocking due to zero time if err := p.SetDeadline(time.Time{}); err != nil { t.Fatal(err) } if want, got := true, s.triggered; want != got { t.Fatalf("unexpected triggered boolean:\n- want: %v\n- got: %v", want, got) } if want, got := false, s.nonblocking; want != got { t.Fatalf("unexpected nonblocking boolean:\n- want: %v\n- got: %v", want, got) } // Reset trigger s.triggered = false // Test 5: socket should remain blocking due to zero time, but not trigger // the system call again if err := p.SetDeadline(time.Time{}); err != nil { t.Fatal(err) } if want, got := false, s.triggered; want != got { t.Fatalf("unexpected triggered boolean:\n- want: %v\n- got: %v", want, got) } if want, got := false, s.nonblocking; want != got { t.Fatalf("unexpected nonblocking boolean:\n- want: %v\n- got: %v", want, got) } } // Test that BPF filter attachment works as intended. type setSockoptSocket struct { setsockopt func(level, name int, v unsafe.Pointer, l uint32) error noopSocket } func (s *setSockoptSocket) SetSockopt(level, name int, v unsafe.Pointer, l uint32) error { return s.setsockopt(level, name, v, l) } func Test_packetConnSetBPF(t *testing.T) { filter, err := bpf.Assemble([]bpf.Instruction{ bpf.RetConstant{Val: 0}, }) if err != nil { t.Fatalf("failed to assemble filter: %v", err) } fn := func(level, name int, _ unsafe.Pointer, _ uint32) error { // Though we can't check the filter itself, we can check the setsockopt // level and name for correctness. if want, got := syscall.SOL_SOCKET, level; want != got { t.Fatalf("unexpected setsockopt level:\n- want: %v\n- got: %v", want, got) } if want, got := syscall.SO_ATTACH_FILTER, name; want != got { t.Fatalf("unexpected setsockopt name:\n- want: %v\n- got: %v", want, got) } return nil } s := &setSockoptSocket{ setsockopt: fn, } p := &packetConn{ s: s, } if err := p.SetBPF(filter); err != nil { t.Fatalf("failed to attach filter: %v", err) } } // testSleeper is a sleeper implementation which atomically increments a // counter to indicate how long it has slept. type testSleeper struct { slept int64 } func (t *testSleeper) Sleep(d time.Duration) { atomic.AddInt64(&t.slept, int64(d)) } // noopSocket is a socket implementation which noops every operation. It is // the basis for more specific socket implementations. type noopSocket struct{} func (noopSocket) Bind(sa syscall.Sockaddr) error { return nil } func (noopSocket) Close() error { return nil } func (noopSocket) FD() int { return 0 } func (noopSocket) Recvfrom(p []byte, flags int) (int, syscall.Sockaddr, error) { return 0, nil, nil } func (noopSocket) Sendto(p []byte, flags int, to syscall.Sockaddr) error { return nil } func (noopSocket) SetNonblock(nonblocking bool) error { return nil } func (noopSocket) SetSockopt(level, name int, v unsafe.Pointer, l uint32) error { return nil } vip-manager-0.6/vendor/github.com/mdlayher/raw/raw_others.go000066400000000000000000000034201353774041100242270ustar00rootroot00000000000000// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd package raw import ( "net" "time" "golang.org/x/net/bpf" ) var ( // Must implement net.PacketConn at compile-time. _ net.PacketConn = &packetConn{} ) // packetConn is the generic implementation of net.PacketConn for this package. type packetConn struct{} // listenPacket is not currently implemented on this platform. func listenPacket(ifi *net.Interface, proto Protocol) (*packetConn, error) { return nil, ErrNotImplemented } // ReadFrom is not currently implemented on this platform. func (p *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, ErrNotImplemented } // WriteTo is not currently implemented on this platform. func (p *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { return 0, ErrNotImplemented } // Close is not currently implemented on this platform. func (p *packetConn) Close() error { return ErrNotImplemented } // LocalAddr is not currently implemented on this platform. func (p *packetConn) LocalAddr() net.Addr { return nil } // SetDeadline is not currently implemented on this platform. func (p *packetConn) SetDeadline(t time.Time) error { return ErrNotImplemented } // SetReadDeadline is not currently implemented on this platform. func (p *packetConn) SetReadDeadline(t time.Time) error { return ErrNotImplemented } // SetWriteDeadline is not currently implemented on this platform. func (p *packetConn) SetWriteDeadline(t time.Time) error { return ErrNotImplemented } // SetBPF is not currently implemented on this platform. func (p *packetConn) SetBPF(filter []bpf.RawInstruction) error { return ErrNotImplemented } // SetPromisc is not currently implemented on this platform. func (p *packetConn) SetPromiscuous(b bool) error { return ErrNotImplemented } vip-manager-0.6/vendor/github.com/mdlayher/raw/sockopt_linux.go000066400000000000000000000006211353774041100247530ustar00rootroot00000000000000// +build linux,!386 package raw import ( "syscall" "unsafe" ) // setsockopt provides access to the setsockopt syscall. func setsockopt(fd, level, name int, v unsafe.Pointer, l uint32) error { _, _, errno := syscall.Syscall6( syscall.SYS_SETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0, ) if errno != 0 { return error(errno) } return nil } vip-manager-0.6/vendor/github.com/mdlayher/raw/sockopt_linux_386.go000066400000000000000000000012161353774041100253540ustar00rootroot00000000000000// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux,386 package raw import ( "syscall" "unsafe" ) const ( sysSETSOCKOPT = 0xe ) func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno) // setsockopt provides access to the setsockopt syscall. func setsockopt(fd, level, name int, v unsafe.Pointer, l uint32) error { _, errno := socketcall( sysSETSOCKOPT, uintptr(fd), uintptr(level), uintptr(name), uintptr(v), uintptr(l), 0, ) if errno != 0 { return error(errno) } return nil } vip-manager-0.6/vendor/github.com/mdlayher/raw/sockopt_linux_386.s000066400000000000000000000003331353774041100252100ustar00rootroot00000000000000// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. TEXT ·socketcall(SB),4,$0-36 JMP syscall·socketcall(SB) vip-manager-0.6/vipconfig/000077500000000000000000000000001353774041100155565ustar00rootroot00000000000000vip-manager-0.6/vipconfig/config.go000066400000000000000000000012311353774041100173470ustar00rootroot00000000000000package vipconfig import () type Config struct { Ip string `yaml:ip` Mask int `yaml:mask` Iface string `yaml:iface` HostingType string `yaml:hosting_type` Key string `yaml:key` Nodename string `yaml:nodename` //hostname to trigger on. usually the name of the host where this vip-manager runs. Endpoint_type string `yaml:endpoint_type` Endpoints []string `yaml:endpoints` Etcd_user string `yaml:etcd_user` Etcd_password string `yaml:etcd_password` Consul_token string `yaml:consul_token` Interval int`yaml:interval` //milliseconds Retry_after int `yaml:retry_after` //milliseconds Retry_num int `yaml:retry_num` } vip-manager-0.6/vipconfig/vip-manager.yml000066400000000000000000000032731353774041100205140ustar00rootroot00000000000000# config for vip-manager by Cybertec Schönig & Schönig GmbH # time (in milliseconds) after which vip-manager wakes up and checks if it needs to register or release ip addresses. interval: 1000 # the etcd or consul key which vip-manager will regularly poll. key: "/service/pgcluster/leader" # if the value of the above key matches the NodeName (often the hostname of this host), vip-manager will try to add the virtual ip address to the interface specified in Iface nodename: "pgcluster_member1" ip: 192.168.0.123 # the virtual ip address to manage mask: 24 # netmask for the virtual ip iface: enp0s3 #interface to which the virtual ip will be added # how the virtual ip should be managed. we currently support "ip addr add/remove" through shell commands or the Hetzner api hosting_type: basic # possible values: basic, hetzner . endpoint_type: etcd # etcd or consul # a list that contains all endpoints to which etcd could talk. endpoints: - http://127.0.0.1:2379 - http://192.168.0.42:2379 # A single list-item is also fine. # consul will always only use the first entry from this list. # For consul, you'll obviously need to change the port to 8500. Unless you're using a different one. Maybe you're a rebel and are running consul on port 2379? Just to confuse people? Why would you do that? Oh, I get it. etcd_user: "patroni" etcd_password: "Julian's secret password" # don't worry about parameter with a prefix that doesn't match the endpoint_type. You can write anything there, I won't even look at it. consul_token: "Julian's secret token" #how often things should be retried and how long to wait between retries. (currently only affects arpClient) retry_num: 2 retry_after: 250 #in milliseconds