diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index dfe930db..9970c368 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -59,6 +59,10 @@ "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/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index 4ee9abb4..98e46a40 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -29,6 +29,7 @@ import ( "github.com/containernetworking/plugins/pkg/ipam" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/utils/sysctl" + "github.com/j-keck/arping" "github.com/vishvananda/netlink" ) @@ -176,36 +177,26 @@ func cmdAdd(args *skel.CmdArgs) error { } result.Interfaces = []*current.Interface{macvlanInterface} - var firstV4Addr net.IP for _, ipc := range result.IPs { // All addresses apply to the container macvlan interface ipc.Interface = 0 - - if ipc.Address.IP.To4() != nil && firstV4Addr == nil { - firstV4Addr = ipc.Address.IP - } } - if firstV4Addr != nil { - err = netns.Do(func(_ ns.NetNS) error { - if err := ip.SetHWAddrByIP(args.IfName, firstV4Addr, nil /* TODO IPv6 */); err != nil { - return err - } - - return ipam.ConfigureIface(args.IfName, result) - }) - if err != nil { + err = netns.Do(func(_ ns.NetNS) error { + if err := ipam.ConfigureIface(args.IfName, result); err != nil { return err } - } - // Re-fetch macvlan interface as its MAC address may have changed - err = netns.Do(func(_ ns.NetNS) error { - link, err := netlink.LinkByName(args.IfName) + contVeth, err := net.InterfaceByName(args.IfName) if err != nil { - return fmt.Errorf("failed to re-fetch macvlan interface: %v", err) + return fmt.Errorf("failed to look up %q: %v", args.IfName, err) + } + + for _, ipc := range result.IPs { + if ipc.Version == "4" { + _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) + } } - macvlanInterface.Mac = link.Attrs().HardwareAddr.String() return nil }) if err != nil { diff --git a/plugins/main/macvlan/macvlan_test.go b/plugins/main/macvlan/macvlan_test.go index 7ae26f30..406264dc 100644 --- a/plugins/main/macvlan/macvlan_test.go +++ b/plugins/main/macvlan/macvlan_test.go @@ -24,7 +24,6 @@ import ( "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" - "github.com/containernetworking/plugins/pkg/utils/hwaddr" "github.com/vishvananda/netlink" @@ -153,9 +152,6 @@ var _ = Describe("macvlan Operations", func() { Expect(err).NotTo(HaveOccurred()) Expect(link.Attrs().Name).To(Equal(IFNAME)) - hwaddrString := fmt.Sprintf("%s", link.Attrs().HardwareAddr) - Expect(hwaddrString).To(HavePrefix(hwaddr.PrivateMACPrefixString)) - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) Expect(err).NotTo(HaveOccurred()) Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index af93312d..42b26707 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -22,8 +22,6 @@ import ( "os" "runtime" - "github.com/vishvananda/netlink" - "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" @@ -32,6 +30,8 @@ import ( "github.com/containernetworking/plugins/pkg/ipam" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/utils" + "github.com/j-keck/arping" + "github.com/vishvananda/netlink" ) func init() { @@ -73,42 +73,18 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu containerInterface.Mac = contVeth0.HardwareAddr.String() containerInterface.Sandbox = netns.Path() - var firstV4Addr net.IP for _, ipc := range pr.IPs { // All addresses apply to the container veth interface ipc.Interface = 1 - - if ipc.Address.IP.To4() != nil && firstV4Addr == nil { - firstV4Addr = ipc.Address.IP - } } pr.Interfaces = []*current.Interface{hostInterface, containerInterface} - if firstV4Addr != nil { - err = hostNS.Do(func(_ ns.NetNS) error { - hostVethName := hostVeth.Name - if err := ip.SetHWAddrByIP(hostVethName, firstV4Addr, nil /* TODO IPv6 */); err != nil { - return fmt.Errorf("failed to set hardware addr by IP: %v", err) - } - - return nil - }) - if err != nil { - return err - } - } - if err = ipam.ConfigureIface(ifName, pr); err != nil { return err } - if err := ip.SetHWAddrByIP(contVeth0.Name, firstV4Addr, nil /* TODO IPv6 */); err != nil { - return fmt.Errorf("failed to set hardware addr by IP: %v", err) - } - - // Re-fetch container veth to update attributes - contVeth, err := netlink.LinkByName(ifName) + contVeth, err := net.InterfaceByName(ifName) if err != nil { return fmt.Errorf("failed to look up %q: %v", ifName, err) } @@ -116,7 +92,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu for _, ipc := range pr.IPs { // Delete the route that was automatically added route := netlink.Route{ - LinkIndex: contVeth.Attrs().Index, + LinkIndex: contVeth.Index, Dst: &net.IPNet{ IP: ipc.Address.IP.Mask(ipc.Address.Mask), Mask: ipc.Address.Mask, @@ -130,7 +106,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu for _, r := range []netlink.Route{ netlink.Route{ - LinkIndex: contVeth.Attrs().Index, + LinkIndex: contVeth.Index, Dst: &net.IPNet{ IP: ipc.Gateway, Mask: net.CIDRMask(32, 32), @@ -139,7 +115,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu Src: ipc.Address.IP, }, netlink.Route{ - LinkIndex: contVeth.Attrs().Index, + LinkIndex: contVeth.Index, Dst: &net.IPNet{ IP: ipc.Address.IP.Mask(ipc.Address.Mask), Mask: ipc.Address.Mask, @@ -155,6 +131,13 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu } } + // Send a gratuitous arp for all v4 addresses + for _, ipc := range pr.IPs { + if ipc.Version == "4" { + _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) + } + } + return nil }) if err != nil { diff --git a/vendor/github.com/j-keck/arping/.gitignore b/vendor/github.com/j-keck/arping/.gitignore new file mode 100644 index 00000000..67065621 --- /dev/null +++ b/vendor/github.com/j-keck/arping/.gitignore @@ -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 index 00000000..887a32ed --- /dev/null +++ b/vendor/github.com/j-keck/arping/LICENSE @@ -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 index 00000000..bdb91d46 --- /dev/null +++ b/vendor/github.com/j-keck/arping/README.md @@ -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 ` +* 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 `. + +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 `. + 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 index 00000000..42ab043d --- /dev/null +++ b/vendor/github.com/j-keck/arping/arp_datagram.go @@ -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 index 00000000..4192e694 --- /dev/null +++ b/vendor/github.com/j-keck/arping/arping.go @@ -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 `. +// +// +// 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 index 00000000..687880ba --- /dev/null +++ b/vendor/github.com/j-keck/arping/arping_bsd.go @@ -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 index 00000000..6e2adfb2 --- /dev/null +++ b/vendor/github.com/j-keck/arping/arping_linux.go @@ -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 index 00000000..abe302d9 --- /dev/null +++ b/vendor/github.com/j-keck/arping/arping_windows.go @@ -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 index 00000000..2b707d3c --- /dev/null +++ b/vendor/github.com/j-keck/arping/netutils.go @@ -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") +}