diff --git a/packetconn.go b/packetconn.go index b923a19..156f143 100644 --- a/packetconn.go +++ b/packetconn.go @@ -23,6 +23,7 @@ type packetConn interface { SetBroadcastFlag() error SetIfIndex(ifIndex int) SetTrafficClass(uint8) error + InstallICMPIDFilter(id int) error } type icmpConn struct { diff --git a/ping.go b/ping.go index c12f2cb..81bf0ad 100644 --- a/ping.go +++ b/ping.go @@ -979,6 +979,13 @@ func (p *Pinger) listen() (packetConn, error) { p.Stop() return nil, err } + + if p.Privileged() { + if err := conn.InstallICMPIDFilter(p.id); err != nil { + p.logger.Warnf("error installing icmp filter, %v", err) + } + } + return conn, nil } diff --git a/ping_test.go b/ping_test.go index 2b2ba9b..975e07b 100644 --- a/ping_test.go +++ b/ping_test.go @@ -677,6 +677,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) InstallICMPIDFilter(id int) error { return nil } func (c testPacketConn) SetIfIndex(ifIndex int) {} func (c testPacketConn) SetTrafficClass(uint8) error { return nil } diff --git a/utils_linux.go b/utils_linux.go index 7810785..c5e9459 100644 --- a/utils_linux.go +++ b/utils_linux.go @@ -9,7 +9,10 @@ import ( "reflect" "syscall" + "golang.org/x/net/bpf" "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) // Returns the length of an ICMP message. @@ -137,6 +140,41 @@ func (c *icmpV6Conn) SetBroadcastFlag() error { ) } +// InstallICMPIDFilter attaches a BPF program to the connection to filter ICMP packets id. +func (c *icmpv4Conn) InstallICMPIDFilter(id int) error { + filter, err := bpf.Assemble([]bpf.Instruction{ + bpf.LoadMemShift{Off: 0}, // Skip IP header + bpf.LoadIndirect{Off: 4, Size: 2}, // Load ICMP echo ident + bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(id), SkipTrue: 0, SkipFalse: 1}, // Jump on ICMP Echo Request (ID check) + bpf.RetConstant{Val: ^uint32(0)}, // If our ID, accept the packet + bpf.LoadIndirect{Off: 0, Size: 1}, // Load ICMP type + bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(ipv4.ICMPTypeEchoReply), SkipTrue: 1, SkipFalse: 0}, // Check if ICMP Echo Reply + bpf.RetConstant{Val: 0xFFFFFFF}, // Accept packet if it's not Echo Reply + bpf.RetConstant{Val: 0}, // Reject Echo packet with wrong identifier + }) + if err != nil { + return err + } + return c.c.IPv4PacketConn().SetBPF(filter) +} + +// InstallICMPIDFilter attaches a BPF program to the connection to filter ICMPv6 packets id. +func (c *icmpV6Conn) InstallICMPIDFilter(id int) error { + filter, err := bpf.Assemble([]bpf.Instruction{ + bpf.LoadAbsolute{Off: 4, Size: 2}, // Load ICMP echo identifier + bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(id), SkipTrue: 0, SkipFalse: 1}, // Check if it matches our identifier + bpf.RetConstant{Val: ^uint32(0)}, // Accept if true + bpf.LoadAbsolute{Off: 0, Size: 1}, // Load ICMP type + bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(ipv6.ICMPTypeEchoReply), SkipTrue: 1, SkipFalse: 0}, // Check if it is an ICMP6 echo reply + bpf.RetConstant{Val: ^uint32(0)}, // Accept if false + bpf.RetConstant{Val: 0}, // Reject if echo with wrong identifier + }) + if err != nil { + return err + } + return c.c.IPv6PacketConn().SetBPF(filter) +} + // getFD gets the system file descriptor for an icmp.PacketConn func getFD(c *icmp.PacketConn) (uintptr, error) { v := reflect.ValueOf(c).Elem().FieldByName("c").Elem() diff --git a/utils_other.go b/utils_other.go index 03fea8b..c358572 100644 --- a/utils_other.go +++ b/utils_other.go @@ -58,3 +58,11 @@ func (c *icmpv4Conn) SetBroadcastFlag() error { func (c *icmpV6Conn) SetBroadcastFlag() error { return nil } + +func (c *icmpv4Conn) InstallICMPIDFilter(id int) error { + return nil +} + +func (c *icmpV6Conn) InstallICMPIDFilter(id int) error { + return nil +} diff --git a/utils_windows.go b/utils_windows.go index cefb239..f507b7a 100644 --- a/utils_windows.go +++ b/utils_windows.go @@ -86,3 +86,11 @@ func (c *icmpv4Conn) SetBroadcastFlag() error { func (c *icmpV6Conn) SetBroadcastFlag() error { return nil } + +func (c *icmpv4Conn) InstallICMPIDFilter(id int) error { + return nil +} + +func (c *icmpV6Conn) InstallICMPIDFilter(id int) error { + return nil +}