Merge pull request #16 from squeed/gratuitous-arp
ptp, macvlan: don't change mac address; send gratuitous arp
This commit is contained in:
commit
5bbff37294
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -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",
|
||||||
|
@ -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 {
|
||||||
|
@ -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))
|
||||||
|
@ -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
1
vendor/github.com/j-keck/arping/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
arping
|
22
vendor/github.com/j-keck/arping/LICENSE
generated
vendored
Normal file
22
vendor/github.com/j-keck/arping/LICENSE
generated
vendored
Normal 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
29
vendor/github.com/j-keck/arping/README.md
generated
vendored
Normal 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
97
vendor/github.com/j-keck/arping/arp_datagram.go
generated
vendored
Normal 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
197
vendor/github.com/j-keck/arping/arping.go
generated
vendored
Normal 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
101
vendor/github.com/j-keck/arping/arping_bsd.go
generated
vendored
Normal 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
38
vendor/github.com/j-keck/arping/arping_linux.go
generated
vendored
Normal 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
28
vendor/github.com/j-keck/arping/arping_windows.go
generated
vendored
Normal 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
64
vendor/github.com/j-keck/arping/netutils.go
generated
vendored
Normal 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")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user