Merge pull request #16 from squeed/gratuitous-arp

ptp, macvlan: don't change mac address; send gratuitous arp
This commit is contained in:
Casey Callendrello 2017-06-16 18:34:37 +02:00 committed by GitHub
commit 5bbff37294
13 changed files with 605 additions and 54 deletions

4
Godeps/Godeps.json generated
View File

@ -59,6 +59,10 @@
"ImportPath": "github.com/d2g/dhcp4client", "ImportPath": "github.com/d2g/dhcp4client",
"Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892" "Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892"
}, },
{
"ImportPath": "github.com/j-keck/arping",
"Rev": "2cf9dc699c5640a7e2c81403a44127bf28033600"
},
{ {
"ImportPath": "github.com/mattn/go-shellwords", "ImportPath": "github.com/mattn/go-shellwords",
"Comment": "v1.0.3", "Comment": "v1.0.3",

View File

@ -29,6 +29,7 @@ import (
"github.com/containernetworking/plugins/pkg/ipam" "github.com/containernetworking/plugins/pkg/ipam"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/utils/sysctl" "github.com/containernetworking/plugins/pkg/utils/sysctl"
"github.com/j-keck/arping"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
@ -176,36 +177,26 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
result.Interfaces = []*current.Interface{macvlanInterface} result.Interfaces = []*current.Interface{macvlanInterface}
var firstV4Addr net.IP
for _, ipc := range result.IPs { for _, ipc := range result.IPs {
// All addresses apply to the container macvlan interface // All addresses apply to the container macvlan interface
ipc.Interface = 0 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 {
err = netns.Do(func(_ ns.NetNS) error { if err := ipam.ConfigureIface(args.IfName, result); err != nil {
if err := ip.SetHWAddrByIP(args.IfName, firstV4Addr, nil /* TODO IPv6 */); err != nil {
return err
}
return ipam.ConfigureIface(args.IfName, result)
})
if err != nil {
return err return err
} }
}
// Re-fetch macvlan interface as its MAC address may have changed contVeth, err := net.InterfaceByName(args.IfName)
err = netns.Do(func(_ ns.NetNS) error {
link, err := netlink.LinkByName(args.IfName)
if err != nil { 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 return nil
}) })
if err != nil { if err != nil {

View File

@ -24,7 +24,6 @@ import (
"github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
@ -153,9 +152,6 @@ var _ = Describe("macvlan Operations", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME)) 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) hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))

View File

@ -22,8 +22,6 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/types/current"
@ -32,6 +30,8 @@ import (
"github.com/containernetworking/plugins/pkg/ipam" "github.com/containernetworking/plugins/pkg/ipam"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/utils" "github.com/containernetworking/plugins/pkg/utils"
"github.com/j-keck/arping"
"github.com/vishvananda/netlink"
) )
func init() { func init() {
@ -73,42 +73,18 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu
containerInterface.Mac = contVeth0.HardwareAddr.String() containerInterface.Mac = contVeth0.HardwareAddr.String()
containerInterface.Sandbox = netns.Path() containerInterface.Sandbox = netns.Path()
var firstV4Addr net.IP
for _, ipc := range pr.IPs { for _, ipc := range pr.IPs {
// All addresses apply to the container veth interface // All addresses apply to the container veth interface
ipc.Interface = 1 ipc.Interface = 1
if ipc.Address.IP.To4() != nil && firstV4Addr == nil {
firstV4Addr = ipc.Address.IP
}
} }
pr.Interfaces = []*current.Interface{hostInterface, containerInterface} 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 { if err = ipam.ConfigureIface(ifName, pr); err != nil {
return err return err
} }
if err := ip.SetHWAddrByIP(contVeth0.Name, firstV4Addr, nil /* TODO IPv6 */); err != nil { contVeth, err := net.InterfaceByName(ifName)
return fmt.Errorf("failed to set hardware addr by IP: %v", err)
}
// Re-fetch container veth to update attributes
contVeth, err := netlink.LinkByName(ifName)
if err != nil { if err != nil {
return fmt.Errorf("failed to look up %q: %v", ifName, err) 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 { for _, ipc := range pr.IPs {
// Delete the route that was automatically added // Delete the route that was automatically added
route := netlink.Route{ route := netlink.Route{
LinkIndex: contVeth.Attrs().Index, LinkIndex: contVeth.Index,
Dst: &net.IPNet{ Dst: &net.IPNet{
IP: ipc.Address.IP.Mask(ipc.Address.Mask), IP: ipc.Address.IP.Mask(ipc.Address.Mask),
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{ for _, r := range []netlink.Route{
netlink.Route{ netlink.Route{
LinkIndex: contVeth.Attrs().Index, LinkIndex: contVeth.Index,
Dst: &net.IPNet{ Dst: &net.IPNet{
IP: ipc.Gateway, IP: ipc.Gateway,
Mask: net.CIDRMask(32, 32), 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, Src: ipc.Address.IP,
}, },
netlink.Route{ netlink.Route{
LinkIndex: contVeth.Attrs().Index, LinkIndex: contVeth.Index,
Dst: &net.IPNet{ Dst: &net.IPNet{
IP: ipc.Address.IP.Mask(ipc.Address.Mask), IP: ipc.Address.IP.Mask(ipc.Address.Mask),
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 return nil
}) })
if err != nil { if err != nil {

1
vendor/github.com/j-keck/arping/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
arping

22
vendor/github.com/j-keck/arping/LICENSE generated vendored Normal file
View File

@ -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.

29
vendor/github.com/j-keck/arping/README.md generated vendored Normal file
View File

@ -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>`.

97
vendor/github.com/j-keck/arping/arp_datagram.go generated vendored Normal file
View File

@ -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
}

197
vendor/github.com/j-keck/arping/arping.go generated vendored Normal file
View File

@ -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
}

101
vendor/github.com/j-keck/arping/arping_bsd.go generated vendored Normal file
View File

@ -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()
}

38
vendor/github.com/j-keck/arping/arping_linux.go generated vendored Normal file
View File

@ -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)
}

28
vendor/github.com/j-keck/arping/arping_windows.go generated vendored Normal file
View File

@ -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
}

64
vendor/github.com/j-keck/arping/netutils.go generated vendored Normal file
View File

@ -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")
}