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",
|
||||
"Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/j-keck/arping",
|
||||
"Rev": "2cf9dc699c5640a7e2c81403a44127bf28033600"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-shellwords",
|
||||
"Comment": "v1.0.3",
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
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