From 3da84b5e7dd93b3eb954aa24ea34c76761f76d60 Mon Sep 17 00:00:00 2001 From: LRH3321 Date: Fri, 10 Jun 2022 14:48:19 +0800 Subject: [PATCH] specify the source IP address --- arping.go | 84 +++++++++++++++++++++++++++++++++-------------------- netutils.go | 3 +- options.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 options.go diff --git a/arping.go b/arping.go index b305731..fe14ebc 100644 --- a/arping.go +++ b/arping.go @@ -72,6 +72,9 @@ var ( // ErrTimeout error ErrTimeout = errors.New("timeout") + // ErrNoUsableInterface no usable interface found + ErrNoUsableInterface = errors.New("no usable interface found") + verboseLog = log.New(ioutil.Discard, "", 0) timeout = time.Duration(500 * time.Millisecond) ) @@ -91,27 +94,40 @@ func Ping(dstIP net.IP) (net.HardwareAddr, time.Duration, error) { // PingOverIfaceByName sends an arp ping over interface name 'ifaceName' to 'dstIP' func PingOverIfaceByName(dstIP net.IP, ifaceName string) (net.HardwareAddr, time.Duration, error) { - if err := validateIP(dstIP); err != nil { - return nil, 0, err - } - - iface, err := net.InterfaceByName(ifaceName) - if err != nil { - return nil, 0, err - } - return PingOverIface(dstIP, *iface) + return PingWithOptions(dstIP, WithIfaceByName(ifaceName)) } // PingOverIface sends an arp ping over interface 'iface' to 'dstIP' func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Duration, error) { + return PingWithOptions(dstIP, WithIface(iface)) +} + +// PingWithOptions sends an arp ping to 'dstIP' +func PingWithOptions(dstIP net.IP, opts ...Option) (net.HardwareAddr, time.Duration, error) { if err := validateIP(dstIP); err != nil { return nil, 0, err } + var ops = newOptions() + for _, opt := range opts { + if err := opt.apply(ops); err != nil { + return nil, 0, err + } + } + + if ops.iface == nil { + return nil, 0, ErrNoUsableInterface + } + iface := *ops.iface + srcIP := ops.sourceIP srcMac := iface.HardwareAddr - srcIP, err := findIPInNetworkFromIface(dstIP, iface) - if err != nil { - return nil, 0, err + + if len(srcIP) == 0 { + ip, err := findIPInNetworkFromIface(dstIP, iface) + if err != nil { + return nil, 0, err + } + srcIP = ip } broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} @@ -162,7 +178,7 @@ func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Du select { case pingResult := <-pingResultChan: return pingResult.mac, pingResult.duration, pingResult.err - case <-time.After(timeout): + case <-time.After(ops.timeout): sock.deinitialize() return nil, 0, ErrTimeout } @@ -170,36 +186,42 @@ func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Du // GratuitousArp sends an gratuitous arp from 'srcIP' func GratuitousArp(srcIP net.IP) error { - if err := validateIP(srcIP); err != nil { - return err - } - - iface, err := findUsableInterfaceForNetwork(srcIP) - if err != nil { - return err - } - return GratuitousArpOverIface(srcIP, *iface) + return GratuitousArpWithOptions(WithSourceIP(srcIP)) } // GratuitousArpOverIfaceByName sends an gratuitous arp over interface name 'ifaceName' from 'srcIP' func GratuitousArpOverIfaceByName(srcIP net.IP, ifaceName string) error { - if err := validateIP(srcIP); err != nil { - return err - } - - iface, err := net.InterfaceByName(ifaceName) - if err != nil { - return err - } - return GratuitousArpOverIface(srcIP, *iface) + return GratuitousArpWithOptions(WithSourceIP(srcIP), WithIfaceByName(ifaceName)) } // GratuitousArpOverIface sends an gratuitous arp over interface 'iface' from 'srcIP' func GratuitousArpOverIface(srcIP net.IP, iface net.Interface) error { + return GratuitousArpWithOptions(WithSourceIP(srcIP), WithIface(iface)) +} + +// GratuitousArpWithOptions sends an gratuitous arp +func GratuitousArpWithOptions(opts ...Option) error { + var ops = newOptions() + for _, opt := range opts { + if err := opt.apply(ops); err != nil { + return err + } + } + + srcIP := ops.sourceIP if err := validateIP(srcIP); err != nil { return err } + if ops.iface == nil { + iface, err := findUsableInterfaceForNetwork(srcIP) + if err != nil { + return err + } + ops.iface = iface + } + iface := *ops.iface + srcMac := iface.HardwareAddr broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} request := newArpRequest(srcMac, srcIP, broadcastMac, srcIP) diff --git a/netutils.go b/netutils.go index 2b707d3..946fc0e 100644 --- a/netutils.go +++ b/netutils.go @@ -1,7 +1,6 @@ package arping import ( - "errors" "fmt" "net" ) @@ -60,5 +59,5 @@ func findUsableInterfaceForNetwork(dstIP net.IP) (*net.Interface, error) { logIfaceResult("USABLE", iface) return &iface, nil } - return nil, errors.New("no usable interface found") + return nil, ErrNoUsableInterface } diff --git a/options.go b/options.go new file mode 100644 index 0000000..4bc955f --- /dev/null +++ b/options.go @@ -0,0 +1,77 @@ +package arping + +import ( + "net" + "time" +) + +type options struct { + iface *net.Interface + sourceIP net.IP + timeout time.Duration +} + +func newOptions() *options { + return &options{timeout: timeout} +} + +// Option configures source ip, timeout, Interface for PingWithOptions +type Option interface { + apply(*options) error +} + +type ifaceName string + +func (n ifaceName) apply(opts *options) error { + iface, err := net.InterfaceByName(string(n)) + if err != nil { + return err + } + if err != nil { + return err + } + opts.iface = iface + return nil +} + +// WithIfaceByName sends an arp ping over interface name 'name' +func WithIfaceByName(name string) Option { + return ifaceName(name) +} + +type netInterface net.Interface + +func (n netInterface) apply(opts *options) error { + opts.iface = (*net.Interface)(&n) + return nil +} + +// WithIface sends an arp ping over interface 'iface' +func WithIface(iface net.Interface) Option { + return netInterface(iface) +} + +type netIP net.IP + +func (n netIP) apply(opts *options) error { + opts.sourceIP = net.IP(n) + return nil +} + +// WithSourceIP sends an arp with source ip address +func WithSourceIP(srcIP net.IP) Option { + return netIP(srcIP) +} + +type duration time.Duration + +func (n duration) apply(opts *options) error { + opts.timeout = time.Duration(n) + return nil +} + +// WithTimeout sets ping timeout +// not works with GratuitousArpWithOptions +func WithTimeout(timeout time.Duration) Option { + return duration(timeout) +}