Skip to content

Commit

Permalink
filter icmp id with sock_filter, to optimize rawsocket preformance
Browse files Browse the repository at this point in the history
Signed-off-by: nvksie <[email protected]>
  • Loading branch information
nvksie committed Dec 4, 2024
1 parent 6d7aa4e commit 13c8bff
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 2 deletions.
1 change: 1 addition & 0 deletions packetconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type packetConn interface {
SetBroadcastFlag() error
SetIfIndex(ifIndex int)
SetTrafficClass(uint8) error
InstallICMPIDFilter(id int) error
}

type icmpConn struct {
Expand Down
10 changes: 8 additions & 2 deletions ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ var (
ipv4Proto = map[string]string{"icmp": "ip4:icmp", "udp": "udp4"}
ipv6Proto = map[string]string{"icmp": "ip6:ipv6-icmp", "udp": "udp6"}

ErrMarkNotSupported = errors.New("setting SO_MARK socket option is not supported on this platform")
ErrDFNotSupported = errors.New("setting do-not-fragment bit is not supported on this platform")
ErrMarkNotSupported = errors.New("setting SO_MARK socket option is not supported on this platform")
ErrDFNotSupported = errors.New("setting do-not-fragment bit is not supported on this platform")
ErrSockFilterNotSupport = errors.New("setting SO_ATTACH_FILTER socket option is not supported on this platform")
)

// New returns a new Pinger struct pointer.
Expand Down Expand Up @@ -979,6 +980,11 @@ func (p *Pinger) listen() (packetConn, error) {
p.Stop()
return nil, err
}

if p.Privileged() {
conn.InstallICMPIDFilter(p.id)

Check failure on line 985 in ping.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `conn.InstallICMPIDFilter` is not checked (errcheck)
}

return conn, nil
}

Expand Down
1 change: 1 addition & 0 deletions ping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down
38 changes: 38 additions & 0 deletions utils_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand Down
8 changes: 8 additions & 0 deletions utils_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,11 @@ func (c *icmpv4Conn) SetBroadcastFlag() error {
func (c *icmpV6Conn) SetBroadcastFlag() error {
return nil
}

func (c *icmpv4Conn) InstallICMPIDFilter(id int) error {
return ErrSockFilterNotSupport
}

func (c *icmpV6Conn) InstallICMPIDFilter(id int) error {
return ErrSockFilterNotSupport
}
8 changes: 8 additions & 0 deletions utils_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,11 @@ func (c *icmpv4Conn) SetBroadcastFlag() error {
func (c *icmpV6Conn) SetBroadcastFlag() error {
return nil
}

func (c *icmpv4Conn) InstallICMPIDFilter(id int) error {
return ErrSockFilterNotSupport
}

func (c *icmpV6Conn) InstallICMPIDFilter(id int) error {
return ErrSockFilterNotSupport
}

0 comments on commit 13c8bff

Please sign in to comment.