Skip to content

Commit 963180e

Browse files
committed
feat: support setting ICMP source address for outgoing packets
Previously, the source IP for ICMP echo requests was determined by system routing. This change applies the Pinger.Source value—already used when binding the socket—to outgoing control messages as well, allowing users to specify which local address ICMP packets originate from. This is useful on multi-interface systems or when testing routing behavior from specific local addresses. Signed-off-by: Steven Normore <[email protected]>
1 parent 0930ed1 commit 963180e

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

packetconn.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type packetConn interface {
2222
SetDoNotFragment() error
2323
SetBroadcastFlag() error
2424
SetIfIndex(ifIndex int)
25+
SetSource(source string)
2526
SetTrafficClass(uint8) error
2627
InstallICMPIDFilter(id int) error
2728
}
@@ -30,6 +31,7 @@ type icmpConn struct {
3031
c *icmp.PacketConn
3132
ttl int
3233
ifIndex int
34+
source string
3335
}
3436

3537
func (c *icmpConn) Close() error {
@@ -44,6 +46,10 @@ func (c *icmpConn) SetIfIndex(ifIndex int) {
4446
c.ifIndex = ifIndex
4547
}
4648

49+
func (c *icmpConn) SetSource(source string) {
50+
c.source = source
51+
}
52+
4753
func (c *icmpConn) SetReadDeadline(t time.Time) error {
4854
return c.c.SetReadDeadline(t)
4955
}
@@ -86,6 +92,13 @@ func (c *icmpv4Conn) WriteTo(b []byte, dst net.Addr) (int, error) {
8692
cm = &ipv4.ControlMessage{IfIndex: c.ifIndex}
8793
}
8894

95+
if c.source != "" {
96+
if cm == nil {
97+
cm = &ipv4.ControlMessage{}
98+
}
99+
cm.Src = net.ParseIP(c.source)
100+
}
101+
89102
return c.c.IPv4PacketConn().WriteTo(b, cm, dst)
90103
}
91104

@@ -131,6 +144,13 @@ func (c *icmpV6Conn) WriteTo(b []byte, dst net.Addr) (int, error) {
131144
cm = &ipv6.ControlMessage{IfIndex: c.ifIndex}
132145
}
133146

147+
if c.source != "" {
148+
if cm == nil {
149+
cm = &ipv6.ControlMessage{}
150+
}
151+
cm.Src = net.ParseIP(c.source)
152+
}
153+
134154
return c.c.IPv6PacketConn().WriteTo(b, cm, dst)
135155
}
136156

ping.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,11 @@ func (p *Pinger) RunWithContext(ctx context.Context) error {
565565
}
566566
conn.SetIfIndex(iface.Index)
567567
}
568+
569+
if p.Source != "" {
570+
conn.SetSource(p.Source)
571+
}
572+
568573
return p.run(ctx, conn)
569574
}
570575

ping_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,17 @@ func TestSetInterfaceName(t *testing.T) {
498498
AssertError(t, err, "device not found")
499499
}
500500

501+
func TestSetSource(t *testing.T) {
502+
pinger := New("localhost")
503+
pinger.Count = 1
504+
pinger.Timeout = time.Second
505+
506+
// Set source address
507+
pinger.Source = "127.0.0.1"
508+
err := pinger.Run()
509+
AssertNoError(t, err)
510+
}
511+
501512
// Test helpers
502513
func makeTestPinger() *Pinger {
503514
pinger := New("127.0.0.1")
@@ -679,6 +690,7 @@ func (c testPacketConn) SetDoNotFragment() error { return nil }
679690
func (c testPacketConn) SetBroadcastFlag() error { return nil }
680691
func (c testPacketConn) InstallICMPIDFilter(id int) error { return nil }
681692
func (c testPacketConn) SetIfIndex(ifIndex int) {}
693+
func (c testPacketConn) SetSource(source string) {}
682694
func (c testPacketConn) SetTrafficClass(uint8) error { return nil }
683695

684696
func (c testPacketConn) ReadFrom(b []byte) (n int, ttl int, src net.Addr, err error) {

0 commit comments

Comments
 (0)