vendor: add j-keck/arping
authorCasey Callendrello <casey.callendrello@coreos.com>
Wed, 7 Jun 2017 13:40:26 +0000 (15:40 +0200)
committerCasey Callendrello <casey.callendrello@coreos.com>
Wed, 7 Jun 2017 13:40:26 +0000 (15:40 +0200)
Godeps/Godeps.json
vendor/github.com/j-keck/arping/.gitignore [new file with mode: 0644]
vendor/github.com/j-keck/arping/LICENSE [new file with mode: 0644]
vendor/github.com/j-keck/arping/README.md [new file with mode: 0644]
vendor/github.com/j-keck/arping/arp_datagram.go [new file with mode: 0644]
vendor/github.com/j-keck/arping/arping.go [new file with mode: 0644]
vendor/github.com/j-keck/arping/arping_bsd.go [new file with mode: 0644]
vendor/github.com/j-keck/arping/arping_linux.go [new file with mode: 0644]
vendor/github.com/j-keck/arping/arping_windows.go [new file with mode: 0644]
vendor/github.com/j-keck/arping/netutils.go [new file with mode: 0644]

index dfe930d..9970c36 100644 (file)
                        "ImportPath": "github.com/d2g/dhcp4client",
                        "Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892"
                },
+               {
+                       "ImportPath": "github.com/j-keck/arping",
+                       "Rev": "2cf9dc699c5640a7e2c81403a44127bf28033600"
+               },
                {
                        "ImportPath": "github.com/mattn/go-shellwords",
                        "Comment": "v1.0.3",
diff --git a/vendor/github.com/j-keck/arping/.gitignore b/vendor/github.com/j-keck/arping/.gitignore
new file mode 100644 (file)
index 0000000..6706562
--- /dev/null
@@ -0,0 +1 @@
+arping
diff --git a/vendor/github.com/j-keck/arping/LICENSE b/vendor/github.com/j-keck/arping/LICENSE
new file mode 100644 (file)
index 0000000..887a32e
--- /dev/null
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+  Copyright (c) 2014-2016 j-keck [jhyphenkeck@gmail.com]
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+  
diff --git a/vendor/github.com/j-keck/arping/README.md b/vendor/github.com/j-keck/arping/README.md
new file mode 100644 (file)
index 0000000..bdb91d4
--- /dev/null
@@ -0,0 +1,29 @@
+# arping
+  
+arping is a native go library to ping a host per arp datagram, or query a host mac address 
+
+The currently supported platforms are: Linux and BSD.
+   
+
+## Usage
+### arping library
+
+* import this library per `import "github.com/j-keck/arping"`
+* export GOPATH if not already (`export GOPATH=$PWD`)
+* download the library `go get`
+* run it `sudo -E go run <YOUR PROGRAMM>` 
+* or build it `go build`
+
+
+The library requires raw socket access. So it must run as root, or with appropriate capabilities under linux: `sudo setcap cap_net_raw+ep <BIN>`.
+
+For api doc and examples see: [godoc](http://godoc.org/github.com/j-keck/arping) or check the standalone under 'cmd/arping/main.go'.
+
+
+    
+### arping executable
+   
+To get a runnable pinger use `go get -u github.com/j-keck/arping/cmd/arping`. This will build the binary in $GOPATH/bin.
+
+arping requires raw socket access. So it must run as root, or with appropriate capabilities under Linux: `sudo setcap cap_net_raw+ep <ARPING_PATH>`.
+
diff --git a/vendor/github.com/j-keck/arping/arp_datagram.go b/vendor/github.com/j-keck/arping/arp_datagram.go
new file mode 100644 (file)
index 0000000..42ab043
--- /dev/null
@@ -0,0 +1,97 @@
+package arping
+
+import (
+       "bytes"
+       "encoding/binary"
+       "net"
+)
+
+const (
+       requestOper  = 1
+       responseOper = 2
+)
+
+type arpDatagram struct {
+       htype uint16 // Hardware Type
+       ptype uint16 // Protocol Type
+       hlen  uint8  // Hardware address Length
+       plen  uint8  // Protocol address length
+       oper  uint16 // Operation 1->request, 2->response
+       sha   []byte // Sender hardware address, length from Hlen
+       spa   []byte // Sender protocol address, length from Plen
+       tha   []byte // Target hardware address, length from Hlen
+       tpa   []byte // Target protocol address, length from Plen
+}
+
+func newArpRequest(
+       srcMac net.HardwareAddr,
+       srcIP net.IP,
+       dstMac net.HardwareAddr,
+       dstIP net.IP) arpDatagram {
+       return arpDatagram{
+               htype: uint16(1),
+               ptype: uint16(0x0800),
+               hlen:  uint8(6),
+               plen:  uint8(4),
+               oper:  uint16(requestOper),
+               sha:   srcMac,
+               spa:   srcIP.To4(),
+               tha:   dstMac,
+               tpa:   dstIP.To4()}
+}
+
+func (datagram arpDatagram) Marshal() []byte {
+       buf := new(bytes.Buffer)
+       binary.Write(buf, binary.BigEndian, datagram.htype)
+       binary.Write(buf, binary.BigEndian, datagram.ptype)
+       binary.Write(buf, binary.BigEndian, datagram.hlen)
+       binary.Write(buf, binary.BigEndian, datagram.plen)
+       binary.Write(buf, binary.BigEndian, datagram.oper)
+       buf.Write(datagram.sha)
+       buf.Write(datagram.spa)
+       buf.Write(datagram.tha)
+       buf.Write(datagram.tpa)
+
+       return buf.Bytes()
+}
+
+func (datagram arpDatagram) MarshalWithEthernetHeader() []byte {
+       // ethernet frame header
+       var ethernetHeader []byte
+       ethernetHeader = append(ethernetHeader, datagram.tha...)
+       ethernetHeader = append(ethernetHeader, datagram.sha...)
+       ethernetHeader = append(ethernetHeader, []byte{0x08, 0x06}...) // arp
+
+       return append(ethernetHeader, datagram.Marshal()...)
+}
+
+func (datagram arpDatagram) SenderIP() net.IP {
+       return net.IP(datagram.spa)
+}
+func (datagram arpDatagram) SenderMac() net.HardwareAddr {
+       return net.HardwareAddr(datagram.sha)
+}
+
+func (datagram arpDatagram) IsResponseOf(request arpDatagram) bool {
+       return datagram.oper == responseOper && bytes.Compare(request.spa, datagram.tpa) == 0
+}
+
+func parseArpDatagram(buffer []byte) arpDatagram {
+       var datagram arpDatagram
+
+       b := bytes.NewBuffer(buffer)
+       binary.Read(b, binary.BigEndian, &datagram.htype)
+       binary.Read(b, binary.BigEndian, &datagram.ptype)
+       binary.Read(b, binary.BigEndian, &datagram.hlen)
+       binary.Read(b, binary.BigEndian, &datagram.plen)
+       binary.Read(b, binary.BigEndian, &datagram.oper)
+
+       haLen := int(datagram.hlen)
+       paLen := int(datagram.plen)
+       datagram.sha = b.Next(haLen)
+       datagram.spa = b.Next(paLen)
+       datagram.tha = b.Next(haLen)
+       datagram.tpa = b.Next(paLen)
+
+       return datagram
+}
diff --git a/vendor/github.com/j-keck/arping/arping.go b/vendor/github.com/j-keck/arping/arping.go
new file mode 100644 (file)
index 0000000..4192e69
--- /dev/null
@@ -0,0 +1,197 @@
+// Package arping is a native go library to ping a host per arp datagram, or query a host mac address
+//
+// The currently supported platforms are: Linux and BSD.
+//
+//
+// The library requires raw socket access. So it must run as root, or with appropriate capabilities under linux:
+// `sudo setcap cap_net_raw+ep <BIN>`.
+//
+//
+// Examples:
+//
+//   ping a host:
+//   ------------
+//     package main
+//     import ("fmt"; "github.com/j-keck/arping"; "net")
+//
+//     func main(){
+//       dstIP := net.ParseIP("192.168.1.1")
+//       if hwAddr, duration, err := arping.Ping(dstIP); err != nil {
+//         fmt.Println(err)
+//       } else {
+//         fmt.Printf("%s (%s) %d usec\n", dstIP, hwAddr, duration/1000)
+//       }
+//     }
+//
+//
+//   resolve mac address:
+//   --------------------
+//     package main
+//     import ("fmt"; "github.com/j-keck/arping"; "net")
+//
+//     func main(){
+//       dstIP := net.ParseIP("192.168.1.1")
+//       if hwAddr, _, err := arping.Ping(dstIP); err != nil {
+//         fmt.Println(err)
+//       } else {
+//         fmt.Printf("%s is at %s\n", dstIP, hwAddr)
+//       }
+//     }
+//
+//
+//   check if host is online:
+//   ------------------------
+//     package main
+//     import ("fmt"; "github.com/j-keck/arping"; "net")
+//
+//     func main(){
+//       dstIP := net.ParseIP("192.168.1.1")
+//       _, _, err := arping.Ping(dstIP)
+//       if err == arping.ErrTimeout {
+//         fmt.Println("offline")
+//       }else if err != nil {
+//         fmt.Println(err.Error())
+//       }else{
+//         fmt.Println("online")
+//       }
+//     }
+//
+package arping
+
+import (
+       "errors"
+       "io/ioutil"
+       "log"
+       "net"
+       "os"
+       "time"
+)
+
+var (
+       // ErrTimeout error
+       ErrTimeout = errors.New("timeout")
+
+       verboseLog = log.New(ioutil.Discard, "", 0)
+       timeout    = time.Duration(500 * time.Millisecond)
+)
+
+// Ping sends an arp ping to 'dstIP'
+func Ping(dstIP net.IP) (net.HardwareAddr, time.Duration, error) {
+       iface, err := findUsableInterfaceForNetwork(dstIP)
+       if err != nil {
+               return nil, 0, err
+       }
+       return PingOverIface(dstIP, *iface)
+}
+
+// PingOverIfaceByName sends an arp ping over interface name 'ifaceName' to 'dstIP'
+func PingOverIfaceByName(dstIP net.IP, ifaceName string) (net.HardwareAddr, time.Duration, error) {
+       iface, err := net.InterfaceByName(ifaceName)
+       if err != nil {
+               return nil, 0, err
+       }
+       return PingOverIface(dstIP, *iface)
+}
+
+// PingOverIface sends an arp ping over interface 'iface' to 'dstIP'
+func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Duration, error) {
+       srcMac := iface.HardwareAddr
+       srcIP, err := findIPInNetworkFromIface(dstIP, iface)
+       if err != nil {
+               return nil, 0, err
+       }
+
+       broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+       request := newArpRequest(srcMac, srcIP, broadcastMac, dstIP)
+
+       if err := initialize(iface); err != nil {
+               return nil, 0, err
+       }
+       defer deinitialize()
+
+       type PingResult struct {
+               mac      net.HardwareAddr
+               duration time.Duration
+               err      error
+       }
+       pingResultChan := make(chan PingResult)
+
+       go func() {
+               // send arp request
+               verboseLog.Printf("arping '%s' over interface: '%s' with address: '%s'\n", dstIP, iface.Name, srcIP)
+               if sendTime, err := send(request); err != nil {
+                       pingResultChan <- PingResult{nil, 0, err}
+               } else {
+                       for {
+                               // receive arp response
+                               response, receiveTime, err := receive()
+
+                               if err != nil {
+                                       pingResultChan <- PingResult{nil, 0, err}
+                                       return
+                               }
+
+                               if response.IsResponseOf(request) {
+                                       duration := receiveTime.Sub(sendTime)
+                                       verboseLog.Printf("process received arp: srcIP: '%s', srcMac: '%s'\n",
+                                               response.SenderIP(), response.SenderMac())
+                                       pingResultChan <- PingResult{response.SenderMac(), duration, err}
+                                       return
+                               }
+
+                               verboseLog.Printf("ignore received arp: srcIP: '%s', srcMac: '%s'\n",
+                                       response.SenderIP(), response.SenderMac())
+                       }
+               }
+       }()
+
+       select {
+       case pingResult := <-pingResultChan:
+               return pingResult.mac, pingResult.duration, pingResult.err
+       case <-time.After(timeout):
+               return nil, 0, ErrTimeout
+       }
+}
+
+// GratuitousArp sends an gratuitous arp from 'srcIP'
+func GratuitousArp(srcIP net.IP) error {
+       iface, err := findUsableInterfaceForNetwork(srcIP)
+       if err != nil {
+               return err
+       }
+       return GratuitousArpOverIface(srcIP, *iface)
+}
+
+// GratuitousArpOverIfaceByName sends an gratuitous arp over interface name 'ifaceName' from 'srcIP'
+func GratuitousArpOverIfaceByName(srcIP net.IP, ifaceName string) error {
+       iface, err := net.InterfaceByName(ifaceName)
+       if err != nil {
+               return err
+       }
+       return GratuitousArpOverIface(srcIP, *iface)
+}
+
+// GratuitousArpOverIface sends an gratuitous arp over interface 'iface' from 'srcIP'
+func GratuitousArpOverIface(srcIP net.IP, iface net.Interface) error {
+       srcMac := iface.HardwareAddr
+       broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+       request := newArpRequest(srcMac, srcIP, broadcastMac, srcIP)
+
+       if err := initialize(iface); err != nil {
+               return err
+       }
+       defer deinitialize()
+       verboseLog.Printf("gratuitous arp over interface: '%s' with address: '%s'\n", iface.Name, srcIP)
+       _, err := send(request)
+       return err
+}
+
+// EnableVerboseLog enables verbose logging on stdout
+func EnableVerboseLog() {
+       verboseLog = log.New(os.Stdout, "", 0)
+}
+
+// SetTimeout sets ping timeout
+func SetTimeout(t time.Duration) {
+       timeout = t
+}
diff --git a/vendor/github.com/j-keck/arping/arping_bsd.go b/vendor/github.com/j-keck/arping/arping_bsd.go
new file mode 100644 (file)
index 0000000..687880b
--- /dev/null
@@ -0,0 +1,101 @@
+// +build darwin freebsd openbsd
+
+package arping
+
+import (
+       "errors"
+       "fmt"
+       "net"
+       "os"
+       "runtime"
+       "syscall"
+       "time"
+)
+
+var bpf *os.File
+var bpfFd int
+var buflen int
+
+var bpfArpFilter = []syscall.BpfInsn{
+       // make sure this is an arp packet
+       *syscall.BpfStmt(syscall.BPF_LD+syscall.BPF_H+syscall.BPF_ABS, 12),
+       *syscall.BpfJump(syscall.BPF_JMP+syscall.BPF_JEQ+syscall.BPF_K, 0x0806, 0, 1),
+       // if we passed all the tests, ask for the whole packet.
+       *syscall.BpfStmt(syscall.BPF_RET+syscall.BPF_K, -1),
+       // otherwise, drop it.
+       *syscall.BpfStmt(syscall.BPF_RET+syscall.BPF_K, 0),
+}
+
+func initialize(iface net.Interface) (err error) {
+       verboseLog.Println("search available /dev/bpfX")
+       for i := 0; i <= 10; i++ {
+               bpfPath := fmt.Sprintf("/dev/bpf%d", i)
+               bpf, err = os.OpenFile(bpfPath, os.O_RDWR, 0666)
+               if err != nil {
+                       verboseLog.Printf("  open failed: %s - %s\n", bpfPath, err.Error())
+               } else {
+                       verboseLog.Printf("  open success: %s\n", bpfPath)
+                       break
+               }
+       }
+       bpfFd = int(bpf.Fd())
+       if bpfFd == -1 {
+               return errors.New("unable to open /dev/bpfX")
+       }
+
+       if err := syscall.SetBpfInterface(bpfFd, iface.Name); err != nil {
+               return err
+       }
+
+       if err := syscall.SetBpfImmediate(bpfFd, 1); err != nil {
+               return err
+       }
+
+       buflen, err = syscall.BpfBuflen(bpfFd)
+       if err != nil {
+               return err
+       }
+
+       if err := syscall.SetBpf(bpfFd, bpfArpFilter); err != nil {
+               return err
+       }
+
+       if err := syscall.FlushBpf(bpfFd); err != nil {
+               return err
+       }
+
+       return nil
+}
+
+func send(request arpDatagram) (time.Time, error) {
+       _, err := syscall.Write(bpfFd, request.MarshalWithEthernetHeader())
+       return time.Now(), err
+}
+
+func receive() (arpDatagram, time.Time, error) {
+       buffer := make([]byte, buflen)
+       n, err := syscall.Read(bpfFd, buffer)
+       if err != nil {
+               return arpDatagram{}, time.Now(), err
+       }
+
+       //
+       // FreeBSD uses a different bpf header (bh_tstamp differ in it's size)
+       // https://www.freebsd.org/cgi/man.cgi?bpf(4)#BPF_HEADER
+       //
+       var bpfHdrLength int
+       if runtime.GOOS == "freebsd" {
+               bpfHdrLength = 26
+       } else {
+               bpfHdrLength = 18
+       }
+
+       // skip bpf header + 14 bytes ethernet header
+       var hdrLength = bpfHdrLength + 14
+
+       return parseArpDatagram(buffer[hdrLength:n]), time.Now(), nil
+}
+
+func deinitialize() error {
+       return bpf.Close()
+}
diff --git a/vendor/github.com/j-keck/arping/arping_linux.go b/vendor/github.com/j-keck/arping/arping_linux.go
new file mode 100644 (file)
index 0000000..6e2adfb
--- /dev/null
@@ -0,0 +1,38 @@
+package arping
+
+import (
+       "net"
+       "syscall"
+       "time"
+)
+
+var sock int
+var toSockaddr syscall.SockaddrLinklayer
+
+func initialize(iface net.Interface) error {
+       toSockaddr = syscall.SockaddrLinklayer{Ifindex: iface.Index}
+
+       // 1544 = htons(ETH_P_ARP)
+       const proto = 1544
+       var err error
+       sock, err = syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, proto)
+       return err
+}
+
+func send(request arpDatagram) (time.Time, error) {
+       return time.Now(), syscall.Sendto(sock, request.MarshalWithEthernetHeader(), 0, &toSockaddr)
+}
+
+func receive() (arpDatagram, time.Time, error) {
+       buffer := make([]byte, 128)
+       n, _, err := syscall.Recvfrom(sock, buffer, 0)
+       if err != nil {
+               return arpDatagram{}, time.Now(), err
+       }
+       // skip 14 bytes ethernet header
+       return parseArpDatagram(buffer[14:n]), time.Now(), nil
+}
+
+func deinitialize() error {
+       return syscall.Close(sock)
+}
diff --git a/vendor/github.com/j-keck/arping/arping_windows.go b/vendor/github.com/j-keck/arping/arping_windows.go
new file mode 100644 (file)
index 0000000..abe302d
--- /dev/null
@@ -0,0 +1,28 @@
+// windows currently not supported.
+// dummy implementation to prevent compilation errors under windows
+
+package arping
+
+import (
+       "errors"
+       "net"
+       "time"
+)
+
+var errWindowsNotSupported = errors.New("arping under windows not supported")
+
+func initialize(iface net.Interface) error {
+       return errWindowsNotSupported
+}
+
+func send(request arpDatagram) (time.Time, error) {
+       return time.Now(), errWindowsNotSupported
+}
+
+func receive() (arpDatagram, time.Time, error) {
+       return arpDatagram{}, time.Now(), errWindowsNotSupported
+}
+
+func deinitialize() error {
+       return errWindowsNotSupported
+}
diff --git a/vendor/github.com/j-keck/arping/netutils.go b/vendor/github.com/j-keck/arping/netutils.go
new file mode 100644 (file)
index 0000000..2b707d3
--- /dev/null
@@ -0,0 +1,64 @@
+package arping
+
+import (
+       "errors"
+       "fmt"
+       "net"
+)
+
+func findIPInNetworkFromIface(dstIP net.IP, iface net.Interface) (net.IP, error) {
+       addrs, err := iface.Addrs()
+
+       if err != nil {
+               return nil, err
+       }
+
+       for _, a := range addrs {
+               if ipnet, ok := a.(*net.IPNet); ok {
+                       if ipnet.Contains(dstIP) {
+                               return ipnet.IP, nil
+                       }
+               }
+       }
+       return nil, fmt.Errorf("iface: '%s' can't reach ip: '%s'", iface.Name, dstIP)
+}
+
+func findUsableInterfaceForNetwork(dstIP net.IP) (*net.Interface, error) {
+       ifaces, err := net.Interfaces()
+
+       if err != nil {
+               return nil, err
+       }
+
+       isDown := func(iface net.Interface) bool {
+               return iface.Flags&1 == 0
+       }
+
+       hasAddressInNetwork := func(iface net.Interface) bool {
+               if _, err := findIPInNetworkFromIface(dstIP, iface); err != nil {
+                       return false
+               }
+               return true
+       }
+
+       verboseLog.Println("search usable interface")
+       logIfaceResult := func(msg string, iface net.Interface) {
+               verboseLog.Printf("%10s: %6s %18s  %s", msg, iface.Name, iface.HardwareAddr, iface.Flags)
+       }
+
+       for _, iface := range ifaces {
+               if isDown(iface) {
+                       logIfaceResult("DOWN", iface)
+                       continue
+               }
+
+               if !hasAddressInNetwork(iface) {
+                       logIfaceResult("OTHER NET", iface)
+                       continue
+               }
+
+               logIfaceResult("USABLE", iface)
+               return &iface, nil
+       }
+       return nil, errors.New("no usable interface found")
+}