Skip to content

Commit 2c9120c

Browse files
committed
Implement a new InterfaceDevice property in the Pinger struct that specifies the device that needs to be used to send and receive ICMP echo requests and response.
This allows usage of `pro-bing` with linux VRFs, and IPv6 link-local addresses. Squashed commit of the following: commit 7385672 Author: Matthieu Pignolet <[email protected]> Date: Tue Oct 29 16:11:33 2024 +0400 Renaming the `Interface` Pinger property to `InterfaceDevice` to avoid conflict with the `interface` keyword. Signed-off-by: Matthieu Pignolet <[email protected]> commit 978c60e Author: Matthieu Pignolet <[email protected]> Date: Tue Oct 29 15:59:36 2024 +0400 Add the `Interface` option to `Pinger` This allows using `pro-bing` to use VRFs interfaces and IPv6 link-local addresses. Originally from @ilolicon in prometheus-community#32. commit 16f9286 Author: Matthieu Pignolet <[email protected]> Date: Tue Oct 29 15:46:13 2024 +0400 Remove the un-used function in te `packetConn` interface that did not get removed during the merging process commit 88bb1f5 Author: ilolicon <[email protected]> Date: Wed Apr 12 16:23:06 2023 +0800 Refactoring the variable name `Iface` to `Interface` and `ifaceIndex` to `ifIndex`(keeping it consistent with `ControlMessage`) Signed-off-by: ilolicon <[email protected]> commit 887b4e2 Author: ilolicon <[email protected]> Date: Wed Apr 12 10:39:36 2023 +0800 feat: interface binding Signed-off-by: ilolicon <[email protected]> Signed-off-by: Matthieu Pignolet <[email protected]>
1 parent a563de4 commit 2c9120c

File tree

5 files changed

+80
-21
lines changed

5 files changed

+80
-21
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ jobs:
1313
use_gomod_cache:
1414
type: boolean
1515
default: true
16-
docker:
17-
- image: cimg/go:<< parameters.go_version >>
16+
machine:
17+
image: ubuntu-2204:2024.05.1
1818
steps:
1919
- checkout
2020
- when:

cmd/ping/ping.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
var usage = `
1414
Usage:
1515
16-
ping [-c count] [-i interval] [-t timeout] [--privileged] host
16+
ping [-c count] [-i interval] [-t timeout] [-I interface] [--privileged] host
1717
1818
Examples:
1919
@@ -29,6 +29,9 @@ Examples:
2929
# ping google for 10 seconds
3030
ping -t 10s www.google.com
3131
32+
# ping google specified interface
33+
ping -I eth1 www.goole.com
34+
3235
# Send a privileged raw ICMP ping
3336
sudo ping --privileged www.google.com
3437
@@ -42,6 +45,7 @@ func main() {
4245
count := flag.Int("c", -1, "")
4346
size := flag.Int("s", 24, "")
4447
ttl := flag.Int("l", 64, "TTL")
48+
iface := flag.String("I", "", "interface name")
4549
privileged := flag.Bool("privileged", false, "")
4650
flag.Usage = func() {
4751
fmt.Print(usage)
@@ -90,6 +94,7 @@ func main() {
9094
pinger.Interval = *interval
9195
pinger.Timeout = *timeout
9296
pinger.TTL = *ttl
97+
pinger.InterfaceDevice = *iface
9398
pinger.SetPrivileged(*privileged)
9499

95100
fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr())

packetconn.go

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ type packetConn interface {
2121
SetMark(m uint) error
2222
SetDoNotFragment() error
2323
SetBroadcastFlag() error
24+
SetIfIndex(ifIndex int)
2425
}
2526

2627
type icmpConn struct {
27-
c *icmp.PacketConn
28-
ttl int
28+
c *icmp.PacketConn
29+
ttl int
30+
ifIndex int
2931
}
3032

3133
func (c *icmpConn) Close() error {
@@ -36,23 +38,12 @@ func (c *icmpConn) SetTTL(ttl int) {
3638
c.ttl = ttl
3739
}
3840

39-
func (c *icmpConn) SetReadDeadline(t time.Time) error {
40-
return c.c.SetReadDeadline(t)
41+
func (c *icmpConn) SetIfIndex(ifIndex int) {
42+
c.ifIndex = ifIndex
4143
}
4244

43-
func (c *icmpConn) WriteTo(b []byte, dst net.Addr) (int, error) {
44-
if c.c.IPv6PacketConn() != nil {
45-
if err := c.c.IPv6PacketConn().SetHopLimit(c.ttl); err != nil {
46-
return 0, err
47-
}
48-
}
49-
if c.c.IPv4PacketConn() != nil {
50-
if err := c.c.IPv4PacketConn().SetTTL(c.ttl); err != nil {
51-
return 0, err
52-
}
53-
}
54-
55-
return c.c.WriteTo(b, dst)
45+
func (c *icmpConn) SetReadDeadline(t time.Time) error {
46+
return c.c.SetReadDeadline(t)
5647
}
5748

5849
type icmpv4Conn struct {
@@ -76,6 +67,22 @@ func (c *icmpv4Conn) ReadFrom(b []byte) (int, int, net.Addr, error) {
7667
return n, ttl, src, err
7768
}
7869

70+
func (c *icmpv4Conn) WriteTo(b []byte, dst net.Addr) (int, error) {
71+
if err := c.c.IPv4PacketConn().SetTTL(c.ttl); err != nil {
72+
return 0, err
73+
}
74+
var cm *ipv4.ControlMessage
75+
if 1 <= c.ifIndex {
76+
// c.ifIndex == 0 if not set interface
77+
if err := c.c.IPv4PacketConn().SetControlMessage(ipv4.FlagInterface, true); err != nil {
78+
return 0, err
79+
}
80+
cm = &ipv4.ControlMessage{IfIndex: c.ifIndex}
81+
}
82+
83+
return c.c.IPv4PacketConn().WriteTo(b, cm, dst)
84+
}
85+
7986
func (c icmpv4Conn) ICMPRequestType() icmp.Type {
8087
return ipv4.ICMPTypeEcho
8188
}
@@ -101,6 +108,22 @@ func (c *icmpV6Conn) ReadFrom(b []byte) (int, int, net.Addr, error) {
101108
return n, ttl, src, err
102109
}
103110

111+
func (c *icmpV6Conn) WriteTo(b []byte, dst net.Addr) (int, error) {
112+
if err := c.c.IPv6PacketConn().SetHopLimit(c.ttl); err != nil {
113+
return 0, err
114+
}
115+
var cm *ipv6.ControlMessage
116+
if 1 <= c.ifIndex {
117+
// c.ifIndex == 0 if not set interface
118+
if err := c.c.IPv6PacketConn().SetControlMessage(ipv6.FlagInterface, true); err != nil {
119+
return 0, err
120+
}
121+
cm = &ipv6.ControlMessage{IfIndex: c.ifIndex}
122+
}
123+
124+
return c.c.IPv6PacketConn().WriteTo(b, cm, dst)
125+
}
126+
104127
func (c icmpV6Conn) ICMPRequestType() icmp.Type {
105128
return ipv6.ICMPTypeEchoRequest
106129
}

ping.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ type Pinger struct {
207207
// Source is the source IP address
208208
Source string
209209

210+
// Interface used to send/recv ICMP messages
211+
InterfaceDevice string
212+
210213
// Channel and mutex used to communicate when the Pinger should stop between goroutines.
211214
done chan interface{}
212215
lock sync.Mutex
@@ -525,6 +528,13 @@ func (p *Pinger) RunWithContext(ctx context.Context) error {
525528
}
526529

527530
conn.SetTTL(p.TTL)
531+
if p.InterfaceDevice != "" {
532+
iface, err := net.InterfaceByName(p.InterfaceDevice)
533+
if err != nil {
534+
return err
535+
}
536+
conn.SetIfIndex(iface.Index)
537+
}
528538
return p.run(ctx, conn)
529539
}
530540

ping_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"errors"
77
"net"
8+
"runtime"
89
"runtime/debug"
910
"sync"
1011
"sync/atomic"
@@ -473,6 +474,26 @@ func TestStatisticsZeroDivision(t *testing.T) {
473474
}
474475
}
475476

477+
func TestSetInterfaceName(t *testing.T) {
478+
pinger := New("localhost")
479+
pinger.Count = 1
480+
pinger.Timeout = time.Second
481+
482+
// Set loopback interface
483+
pinger.InterfaceDevice = "lo"
484+
err := pinger.Run()
485+
if runtime.GOOS == "linux" {
486+
AssertNoError(t, err)
487+
} else {
488+
AssertError(t, err, "other platforms unsupport this feature")
489+
}
490+
491+
// Set fake interface
492+
pinger.InterfaceDevice = "L()0pB@cK"
493+
err = pinger.Run()
494+
AssertError(t, err, "device not found")
495+
}
496+
476497
// Test helpers
477498
func makeTestPinger() *Pinger {
478499
pinger := New("127.0.0.1")
@@ -644,7 +665,7 @@ func (c testPacketConn) SetTTL(t int) {}
644665
func (c testPacketConn) SetMark(m uint) error { return nil }
645666
func (c testPacketConn) SetDoNotFragment() error { return nil }
646667
func (c testPacketConn) SetBroadcastFlag() error { return nil }
647-
668+
func (c testPacketConn) SetIfIndex(ifIndex int) {}
648669
func (c testPacketConn) ReadFrom(b []byte) (n int, ttl int, src net.Addr, err error) {
649670
return 0, 0, testAddr, nil
650671
}

0 commit comments

Comments
 (0)