From c9bae6566840b7d949f4ddaf9d7919ca29781cce Mon Sep 17 00:00:00 2001 From: Matthieu Pignolet Date: Tue, 29 Oct 2024 21:26:17 +0400 Subject: [PATCH] Squashed commit of the following: commit 75cbb266f577f7f942b6722123e3b9efe23d460a Author: Matthieu Pignolet Date: Tue Oct 29 20:49:25 2024 +0400 Use the machine executor for CI Signed-off-by: Matthieu Pignolet commit a0e098acf018c956a199c233e0846fa98a27a12c Author: Matthieu Pignolet Date: Tue Oct 29 20:48:33 2024 +0400 Using the machine executor for CI Signed-off-by: Matthieu Pignolet commit 77e106595be1379170f687be8d1343d0ba7efcc1 Merge: 3cb9e40 a563de4 Author: Matthieu Pignolet Date: Tue Oct 29 20:09:52 2024 +0400 Merge branch 'prometheus-community:main' into main commit 3cb9e4060a9dabe3727653eac7c11c8dae0f8167 Author: Matthieu Pignolet Date: Tue Oct 29 20:05:12 2024 +0400 Remote the un-used workaround used to avoid the `lo` interfaces Signed-off-by: Matthieu Pignolet commit 006d1737a7f686b1291456d8ea7baf5c7f613e05 Author: Matthieu Pignolet Date: Tue Oct 29 17:11:14 2024 +0400 add a function (`testSetInterfaceNameChooseInterface`) to choose any interface that has an IP address assigned to it this is a "hack" to supposedly make CircleCI happy Signed-off-by: Matthieu Pignolet commit 7385672db6ceee3d9b306ea1bc76f45c3ed9fe51 Author: Matthieu Pignolet 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 commit 978c60e193d3f5ee90e864e192efb4902e756882 Author: Matthieu Pignolet 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 #32. commit 16f9286c5342426490fc7bdf0d4c2d98bfe125e2 Author: Matthieu Pignolet 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 88bb1f584f48df6a457d61fbc6d88cfadb44ed37 Author: ilolicon <97431110@qq.com> 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 <97431110@qq.com> commit 887b4e20da2d554aaa54f9758c46f80e8abb4544 Author: ilolicon <97431110@qq.com> Date: Wed Apr 12 10:39:36 2023 +0800 feat: interface binding Signed-off-by: ilolicon <97431110@qq.com> Signed-off-by: Matthieu Pignolet Signed-off-by: Matthieu Pignolet --- .circleci/config.yml | 4 ++-- cmd/ping/ping.go | 7 +++++- packetconn.go | 57 +++++++++++++++++++++++++++++++------------- ping.go | 10 ++++++++ ping_test.go | 23 +++++++++++++++++- 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a49fc38..3004057 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,8 +13,8 @@ jobs: use_gomod_cache: type: boolean default: true - docker: - - image: cimg/go:<< parameters.go_version >> + machine: + image: ubuntu-2204:2024.05.1 steps: - checkout - when: diff --git a/cmd/ping/ping.go b/cmd/ping/ping.go index 645f2e2..0b29eb6 100644 --- a/cmd/ping/ping.go +++ b/cmd/ping/ping.go @@ -13,7 +13,7 @@ import ( var usage = ` Usage: - ping [-c count] [-i interval] [-t timeout] [--privileged] host + ping [-c count] [-i interval] [-t timeout] [-I interface] [--privileged] host Examples: @@ -29,6 +29,9 @@ Examples: # ping google for 10 seconds ping -t 10s www.google.com + # ping google specified interface + ping -I eth1 www.goole.com + # Send a privileged raw ICMP ping sudo ping --privileged www.google.com @@ -42,6 +45,7 @@ func main() { count := flag.Int("c", -1, "") size := flag.Int("s", 24, "") ttl := flag.Int("l", 64, "TTL") + iface := flag.String("I", "", "interface name") privileged := flag.Bool("privileged", false, "") flag.Usage = func() { fmt.Print(usage) @@ -90,6 +94,7 @@ func main() { pinger.Interval = *interval pinger.Timeout = *timeout pinger.TTL = *ttl + pinger.InterfaceDevice = *iface pinger.SetPrivileged(*privileged) fmt.Printf("PING %s (%s):\n", pinger.Addr(), pinger.IPAddr()) diff --git a/packetconn.go b/packetconn.go index c4ca820..8528d79 100644 --- a/packetconn.go +++ b/packetconn.go @@ -21,11 +21,13 @@ type packetConn interface { SetMark(m uint) error SetDoNotFragment() error SetBroadcastFlag() error + SetIfIndex(ifIndex int) } type icmpConn struct { - c *icmp.PacketConn - ttl int + c *icmp.PacketConn + ttl int + ifIndex int } func (c *icmpConn) Close() error { @@ -36,23 +38,12 @@ func (c *icmpConn) SetTTL(ttl int) { c.ttl = ttl } -func (c *icmpConn) SetReadDeadline(t time.Time) error { - return c.c.SetReadDeadline(t) +func (c *icmpConn) SetIfIndex(ifIndex int) { + c.ifIndex = ifIndex } -func (c *icmpConn) WriteTo(b []byte, dst net.Addr) (int, error) { - if c.c.IPv6PacketConn() != nil { - if err := c.c.IPv6PacketConn().SetHopLimit(c.ttl); err != nil { - return 0, err - } - } - if c.c.IPv4PacketConn() != nil { - if err := c.c.IPv4PacketConn().SetTTL(c.ttl); err != nil { - return 0, err - } - } - - return c.c.WriteTo(b, dst) +func (c *icmpConn) SetReadDeadline(t time.Time) error { + return c.c.SetReadDeadline(t) } type icmpv4Conn struct { @@ -76,6 +67,22 @@ func (c *icmpv4Conn) ReadFrom(b []byte) (int, int, net.Addr, error) { return n, ttl, src, err } +func (c *icmpv4Conn) WriteTo(b []byte, dst net.Addr) (int, error) { + if err := c.c.IPv4PacketConn().SetTTL(c.ttl); err != nil { + return 0, err + } + var cm *ipv4.ControlMessage + if 1 <= c.ifIndex { + // c.ifIndex == 0 if not set interface + if err := c.c.IPv4PacketConn().SetControlMessage(ipv4.FlagInterface, true); err != nil { + return 0, err + } + cm = &ipv4.ControlMessage{IfIndex: c.ifIndex} + } + + return c.c.IPv4PacketConn().WriteTo(b, cm, dst) +} + func (c icmpv4Conn) ICMPRequestType() icmp.Type { return ipv4.ICMPTypeEcho } @@ -101,6 +108,22 @@ func (c *icmpV6Conn) ReadFrom(b []byte) (int, int, net.Addr, error) { return n, ttl, src, err } +func (c *icmpV6Conn) WriteTo(b []byte, dst net.Addr) (int, error) { + if err := c.c.IPv6PacketConn().SetHopLimit(c.ttl); err != nil { + return 0, err + } + var cm *ipv6.ControlMessage + if 1 <= c.ifIndex { + // c.ifIndex == 0 if not set interface + if err := c.c.IPv6PacketConn().SetControlMessage(ipv6.FlagInterface, true); err != nil { + return 0, err + } + cm = &ipv6.ControlMessage{IfIndex: c.ifIndex} + } + + return c.c.IPv6PacketConn().WriteTo(b, cm, dst) +} + func (c icmpV6Conn) ICMPRequestType() icmp.Type { return ipv6.ICMPTypeEchoRequest } diff --git a/ping.go b/ping.go index 9175192..8a58517 100644 --- a/ping.go +++ b/ping.go @@ -207,6 +207,9 @@ type Pinger struct { // Source is the source IP address Source string + // Interface used to send/recv ICMP messages + InterfaceDevice string + // Channel and mutex used to communicate when the Pinger should stop between goroutines. done chan interface{} lock sync.Mutex @@ -525,6 +528,13 @@ func (p *Pinger) RunWithContext(ctx context.Context) error { } conn.SetTTL(p.TTL) + if p.InterfaceDevice != "" { + iface, err := net.InterfaceByName(p.InterfaceDevice) + if err != nil { + return err + } + conn.SetIfIndex(iface.Index) + } return p.run(ctx, conn) } diff --git a/ping_test.go b/ping_test.go index bbbfe9b..7b775c8 100644 --- a/ping_test.go +++ b/ping_test.go @@ -5,6 +5,7 @@ import ( "context" "errors" "net" + "runtime" "runtime/debug" "sync" "sync/atomic" @@ -473,6 +474,26 @@ func TestStatisticsZeroDivision(t *testing.T) { } } +func TestSetInterfaceName(t *testing.T) { + pinger := New("localhost") + pinger.Count = 1 + pinger.Timeout = time.Second + + // Set loopback interface + pinger.InterfaceDevice = "lo" + err := pinger.Run() + if runtime.GOOS == "linux" { + AssertNoError(t, err) + } else { + AssertError(t, err, "other platforms unsupport this feature") + } + + // Set fake interface + pinger.InterfaceDevice = "L()0pB@cK" + err = pinger.Run() + AssertError(t, err, "device not found") +} + // Test helpers func makeTestPinger() *Pinger { pinger := New("127.0.0.1") @@ -644,7 +665,7 @@ func (c testPacketConn) SetTTL(t int) {} func (c testPacketConn) SetMark(m uint) error { return nil } func (c testPacketConn) SetDoNotFragment() error { return nil } func (c testPacketConn) SetBroadcastFlag() error { return nil } - +func (c testPacketConn) SetIfIndex(ifIndex int) {} func (c testPacketConn) ReadFrom(b []byte) (n int, ttl int, src net.Addr, err error) { return 0, 0, testAddr, nil }