Skip to content

Commit d68554f

Browse files
author
Zack Liu
committed
specify the source IP address
1 parent c50f431 commit d68554f

3 files changed

Lines changed: 111 additions & 33 deletions

File tree

arping.go

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ var (
7272
// ErrTimeout error
7373
ErrTimeout = errors.New("timeout")
7474

75+
// ErrNoUsableInterface no usable interface found
76+
ErrNoUsableInterface = errors.New("no usable interface found")
77+
7578
verboseLog = log.New(ioutil.Discard, "", 0)
7679
timeout = time.Duration(500 * time.Millisecond)
7780
)
@@ -91,27 +94,45 @@ func Ping(dstIP net.IP) (net.HardwareAddr, time.Duration, error) {
9194

9295
// PingOverIfaceByName sends an arp ping over interface name 'ifaceName' to 'dstIP'
9396
func PingOverIfaceByName(dstIP net.IP, ifaceName string) (net.HardwareAddr, time.Duration, error) {
94-
if err := validateIP(dstIP); err != nil {
95-
return nil, 0, err
96-
}
97-
98-
iface, err := net.InterfaceByName(ifaceName)
99-
if err != nil {
100-
return nil, 0, err
101-
}
102-
return PingOverIface(dstIP, *iface)
97+
return PingWithOptions(dstIP, WithIfaceByName(ifaceName))
10398
}
10499

105100
// PingOverIface sends an arp ping over interface 'iface' to 'dstIP'
106101
func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Duration, error) {
102+
return PingWithOptions(dstIP, WithIface(iface))
103+
}
104+
105+
// PingWithOptions sends an arp ping to 'dstIP'
106+
func PingWithOptions(dstIP net.IP, options ...Option) (net.HardwareAddr, time.Duration, error) {
107107
if err := validateIP(dstIP); err != nil {
108108
return nil, 0, err
109109
}
110110

111+
var op = new(Op)
112+
for _, f := range options {
113+
if err := f(op); err != nil {
114+
return nil, 0, err
115+
}
116+
}
117+
118+
if op.timeout == 0 {
119+
op.timeout = timeout
120+
}
121+
122+
if op.iface == nil {
123+
return nil, 0, ErrNoUsableInterface
124+
}
125+
iface := *op.iface
126+
srcIP := op.srcIP
127+
111128
srcMac := iface.HardwareAddr
112-
srcIP, err := findIPInNetworkFromIface(dstIP, iface)
113-
if err != nil {
114-
return nil, 0, err
129+
130+
if len(srcIP) == 0 {
131+
ip, err := findIPInNetworkFromIface(dstIP, iface)
132+
if err != nil {
133+
return nil, 0, err
134+
}
135+
srcIP = ip
115136
}
116137

117138
broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
@@ -162,44 +183,50 @@ func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Du
162183
select {
163184
case pingResult := <-pingResultChan:
164185
return pingResult.mac, pingResult.duration, pingResult.err
165-
case <-time.After(timeout):
186+
case <-time.After(op.timeout):
166187
sock.deinitialize()
167188
return nil, 0, ErrTimeout
168189
}
169190
}
170191

171192
// GratuitousArp sends an gratuitous arp from 'srcIP'
172193
func GratuitousArp(srcIP net.IP) error {
173-
if err := validateIP(srcIP); err != nil {
174-
return err
175-
}
176-
177-
iface, err := findUsableInterfaceForNetwork(srcIP)
178-
if err != nil {
179-
return err
180-
}
181-
return GratuitousArpOverIface(srcIP, *iface)
194+
return GratuitousArpWithOptions(WithSourceIP(srcIP))
182195
}
183196

184197
// GratuitousArpOverIfaceByName sends an gratuitous arp over interface name 'ifaceName' from 'srcIP'
185198
func GratuitousArpOverIfaceByName(srcIP net.IP, ifaceName string) error {
186-
if err := validateIP(srcIP); err != nil {
187-
return err
188-
}
189-
190-
iface, err := net.InterfaceByName(ifaceName)
191-
if err != nil {
192-
return err
193-
}
194-
return GratuitousArpOverIface(srcIP, *iface)
199+
return GratuitousArpWithOptions(WithSourceIP(srcIP), WithIfaceByName(ifaceName))
195200
}
196201

197202
// GratuitousArpOverIface sends an gratuitous arp over interface 'iface' from 'srcIP'
198203
func GratuitousArpOverIface(srcIP net.IP, iface net.Interface) error {
204+
return GratuitousArpWithOptions(WithSourceIP(srcIP), WithIface(iface))
205+
}
206+
207+
// GratuitousArpWithOptions sends an gratuitous arp
208+
func GratuitousArpWithOptions(options ...Option) error {
209+
var op = new(Op)
210+
for _, f := range options {
211+
if err := f(op); err != nil {
212+
return err
213+
}
214+
}
215+
216+
srcIP := op.srcIP
199217
if err := validateIP(srcIP); err != nil {
200218
return err
201219
}
202220

221+
if op.iface == nil {
222+
iface, err := findUsableInterfaceForNetwork(srcIP)
223+
if err != nil {
224+
return err
225+
}
226+
op.iface = iface
227+
}
228+
iface := *op.iface
229+
203230
srcMac := iface.HardwareAddr
204231
broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
205232
request := newArpRequest(srcMac, srcIP, broadcastMac, srcIP)

netutils.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package arping
22

33
import (
4-
"errors"
54
"fmt"
65
"net"
76
)
@@ -60,5 +59,5 @@ func findUsableInterfaceForNetwork(dstIP net.IP) (*net.Interface, error) {
6059
logIfaceResult("USABLE", iface)
6160
return &iface, nil
6261
}
63-
return nil, errors.New("no usable interface found")
62+
return nil, ErrNoUsableInterface
6463
}

option.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package arping
2+
3+
import (
4+
"net"
5+
"time"
6+
)
7+
8+
type Op struct {
9+
iface *net.Interface
10+
timeout time.Duration
11+
srcIP net.IP
12+
}
13+
14+
type Option func(op *Op) error
15+
16+
// WithIfaceByName sends an arp ping over interface name 'ifaceName'
17+
func WithIfaceByName(ifaceName string) Option {
18+
return func(op *Op) error {
19+
iface, err := net.InterfaceByName(ifaceName)
20+
if err != nil {
21+
return err
22+
}
23+
24+
op.iface = iface
25+
return nil
26+
}
27+
}
28+
29+
// WithIface sends an arp ping over interface 'iface'
30+
func WithIface(iface net.Interface) Option {
31+
return func(op *Op) error {
32+
op.iface = &iface
33+
return nil
34+
}
35+
}
36+
37+
// WithSourceIP sends an arp with source ip address
38+
func WithSourceIP(srcIP net.IP) Option {
39+
return func(op *Op) error {
40+
op.srcIP = srcIP
41+
return nil
42+
}
43+
}
44+
45+
// WithTimeout sets ping timeout
46+
// not works with GratuitousArpWithOptions
47+
func WithTimeout(timeout time.Duration) Option {
48+
return func(op *Op) error {
49+
op.timeout = timeout
50+
return nil
51+
}
52+
}

0 commit comments

Comments
 (0)