Skip to content

Commit

Permalink
Refactor kprobe tracing functionality out of system/socket
Browse files Browse the repository at this point in the history
This creates a new package, x-pack/auditbeat/tracing/kprobes, which provides
high-level functionality to create kprobe-tracing based solutions.

It extracts the kprobe-templating and guessing functionality out of the
system/socket dataset and integrates it into an easier to use interface.
  • Loading branch information
adriansr committed Sep 8, 2020
1 parent 6aa800c commit 369cf01
Show file tree
Hide file tree
Showing 35 changed files with 916 additions and 738 deletions.
12 changes: 6 additions & 6 deletions x-pack/auditbeat/module/system/socket/guess/creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"golang.org/x/sys/unix"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

/*
Expand Down Expand Up @@ -57,7 +57,7 @@ func init() {
}

type guessStructCreds struct {
ctx Context
ctx kprobes.GuessContext
}

// Name of this guess.
Expand All @@ -84,22 +84,22 @@ func (g *guessStructCreds) Requires() []string {

// Probes returns a kretprobe on prepare_creds that dumps the first bytes
// pointed to by the return value, which is a struct cred.
func (g *guessStructCreds) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
func (g *guessStructCreds) Probes() ([]kprobes.ProbeDef, error) {
return []kprobes.ProbeDef{
{
Probe: tracing.Probe{
Type: tracing.TypeKRetProbe,
Name: "guess_struct_creds",
Address: "prepare_creds",
Fetchargs: helper.MakeMemoryDump("{{.RET}}", 0, credDumpBytes),
Fetchargs: kprobes.MakeMemoryDump("{{.RET}}", 0, credDumpBytes),
},
Decoder: tracing.NewDumpDecoder,
},
}, nil
}

// Prepare is a no-op.
func (g *guessStructCreds) Prepare(ctx Context) error {
func (g *guessStructCreds) Prepare(ctx kprobes.GuessContext) error {
return nil
}

Expand Down
17 changes: 9 additions & 8 deletions x-pack/auditbeat/module/system/socket/guess/cskxmit6.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

/*
Expand All @@ -40,7 +41,7 @@ func init() {
}

type guessInet6CskXmit struct {
ctx Context
ctx kprobes.GuessContext
loopback helper.IPv6Loopback
clientAddr, serverAddr unix.SockaddrInet6
client, server int
Expand Down Expand Up @@ -70,38 +71,38 @@ func (g *guessInet6CskXmit) Requires() []string {
}

// Condition allows this probe to run only when IPv6 is enabled.
func (g *guessInet6CskXmit) Condition(ctx Context) (bool, error) {
func (g *guessInet6CskXmit) Condition(ctx kprobes.GuessContext) (bool, error) {
return isIPv6Enabled(ctx.Vars)
}

// Probes returns 2 probes:
// - kretprobe on inet_csk_accept, which returns a struct sock*
// - kprobe on inet6_csk_xmit, returning 1st argument as pointer and dump.
func (g *guessInet6CskXmit) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
func (g *guessInet6CskXmit) Probes() ([]kprobes.ProbeDef, error) {
return []kprobes.ProbeDef{
{
Probe: tracing.Probe{
Type: tracing.TypeKRetProbe,
Name: "inet_csk_accept_guess",
Address: "inet_csk_accept",
Fetchargs: "sock={{.RET}}",
},
Decoder: helper.NewStructDecoder(func() interface{} { return new(sockArgumentGuess) }),
Decoder: kprobes.NewStructDecoder(func() interface{} { return new(sockArgumentGuess) }),
},
{
Probe: tracing.Probe{
Name: "inet6_csk_xmit_guess",
Address: "inet6_csk_xmit",
Fetchargs: "arg={{.P1}} dump=" + helper.MakeMemoryDump("{{.P1}}", 0, skbuffDumpSize),
Fetchargs: "arg={{.P1}} dump=" + kprobes.MakeMemoryDump("{{.P1}}", 0, skbuffDumpSize),
},
Decoder: helper.NewStructDecoder(func() interface{} { return new(skbuffSockGuess) }),
Decoder: kprobes.NewStructDecoder(func() interface{} { return new(skbuffSockGuess) }),
},
}, nil
}

// Prepare setups an IPv6 TCP client/server where the server is listening
// and the client is connecting to it.
func (g *guessInet6CskXmit) Prepare(ctx Context) (err error) {
func (g *guessInet6CskXmit) Prepare(ctx kprobes.GuessContext) (err error) {
g.ctx = ctx
g.acceptedFd = -1
g.loopback, err = helper.NewIPv6Loopback()
Expand Down
14 changes: 7 additions & 7 deletions x-pack/auditbeat/module/system/socket/guess/deref.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"syscall"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

/*
Expand All @@ -39,14 +39,14 @@ const (
)

type guessDeref struct {
ctx Context
ctx kprobes.GuessContext
tries int
garbage bool
}

// Condition allows the guess to run if the environment variable is set to a
// decimal value greater than zero.
func (g *guessDeref) Condition(ctx Context) (run bool, err error) {
func (g *guessDeref) Condition(ctx kprobes.GuessContext) (run bool, err error) {
v := os.Getenv(envVar)
if v == "" {
return false, nil
Expand Down Expand Up @@ -79,22 +79,22 @@ func (g *guessDeref) Requires() []string {

// Probes returns a kprobe on uname() that dumps the first bytes
// pointed to by its first parameter.
func (g *guessDeref) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
func (g *guessDeref) Probes() ([]kprobes.ProbeDef, error) {
return []kprobes.ProbeDef{
{
Probe: tracing.Probe{
Type: tracing.TypeKProbe,
Name: "guess_null_ptr_deref",
Address: "{{.SYS_UNAME}}",
Fetchargs: helper.MakeMemoryDump("{{.SYS_P1}}", 0, credDumpBytes),
Fetchargs: kprobes.MakeMemoryDump("{{.SYS_P1}}", 0, credDumpBytes),
},
Decoder: tracing.NewDumpDecoder,
},
}, nil
}

// Prepare is a no-op.
func (g *guessDeref) Prepare(ctx Context) error {
func (g *guessDeref) Prepare(ctx kprobes.GuessContext) error {
g.ctx = ctx
return nil
}
Expand Down
12 changes: 12 additions & 0 deletions x-pack/auditbeat/module/system/socket/guess/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,15 @@ func (cs *inetClientServer) Cleanup() error {
unix.Close(cs.client)
return nil
}

func isIPv6Enabled(vars common.MapStr) (bool, error) {
iface, err := vars.GetValue("HAS_IPV6")
if err != nil {
return false, err
}
hasIPv6, ok := iface.(bool)
if !ok {
return false, errors.New("HAS_IPV6 is not a bool")
}
return hasIPv6, nil
}
12 changes: 6 additions & 6 deletions x-pack/auditbeat/module/system/socket/guess/inetsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"golang.org/x/sys/unix"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

// Guess the offsets within a struct inet_sock where the local and remote
Expand All @@ -41,7 +41,7 @@ func init() {
}

type guessInetSockIPv4 struct {
ctx Context
ctx kprobes.GuessContext
local, remote unix.SockaddrInet4
server, client int
}
Expand Down Expand Up @@ -74,14 +74,14 @@ func (g *guessInetSockIPv4) Requires() []string {

// Probes returns a kretprobe on inet_sock_accept that dumps the return
// value (an inet_sock*).
func (g *guessInetSockIPv4) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
func (g *guessInetSockIPv4) Probes() ([]kprobes.ProbeDef, error) {
return []kprobes.ProbeDef{
{
Probe: tracing.Probe{
Type: tracing.TypeKRetProbe,
Name: "inet_sock_guess",
Address: "inet_csk_accept",
Fetchargs: helper.MakeMemoryDump("{{.RET}}", 0, 2048),
Fetchargs: kprobes.MakeMemoryDump("{{.RET}}", 0, 2048),
},
Decoder: tracing.NewDumpDecoder,
},
Expand All @@ -90,7 +90,7 @@ func (g *guessInetSockIPv4) Probes() ([]helper.ProbeDef, error) {

// Prepare creates a TCP/IP client and server bound to random loopback addresses
// (127.x.x.x).
func (g *guessInetSockIPv4) Prepare(ctx Context) (err error) {
func (g *guessInetSockIPv4) Prepare(ctx kprobes.GuessContext) (err error) {
g.ctx = ctx
g.local = unix.SockaddrInet4{
Port: 0,
Expand Down
15 changes: 8 additions & 7 deletions x-pack/auditbeat/module/system/socket/guess/inetsock6.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

/*
Expand Down Expand Up @@ -109,7 +110,7 @@ func init() {
}

type guessInetSockIPv6 struct {
ctx Context
ctx kprobes.GuessContext
loopback helper.IPv6Loopback
clientAddr, serverAddr unix.SockaddrInet6
client, server int
Expand Down Expand Up @@ -143,7 +144,7 @@ func (g *guessInetSockIPv6) Requires() []string {
}

// Condition allows this probe to run only when IPv6 is enabled.
func (g *guessInetSockIPv6) Condition(ctx Context) (bool, error) {
func (g *guessInetSockIPv6) Condition(ctx kprobes.GuessContext) (bool, error) {
runs, err := isIPv6Enabled(ctx.Vars)
if err != nil {
return false, err
Expand Down Expand Up @@ -178,13 +179,13 @@ func (d *decoderWrapper) Decode(raw []byte, meta tracing.Metadata) (event interf
// Probes returns a kretprobe in inet_csk_accept that dumps the memory pointed
// to by the return value (an inet_sock*) and a kretprobe that dumps various
// candidates for the ipv6_pinfo struct.
func (g *guessInetSockIPv6) Probes() (probes []helper.ProbeDef, err error) {
probes = append(probes, helper.ProbeDef{
func (g *guessInetSockIPv6) Probes() (probes []kprobes.ProbeDef, err error) {
probes = append(probes, kprobes.ProbeDef{
Probe: tracing.Probe{
Type: tracing.TypeKRetProbe,
Name: "inet_sock_ipv6_guess",
Address: "inet_csk_accept",
Fetchargs: helper.MakeMemoryDump("{{.RET}}", 0, inetSockDumpSize),
Fetchargs: kprobes.MakeMemoryDump("{{.RET}}", 0, inetSockDumpSize),
},
Decoder: tracing.NewDumpDecoder,
})
Expand All @@ -204,7 +205,7 @@ func (g *guessInetSockIPv6) Probes() (probes []helper.ProbeDef, err error) {
// dumps the rcv_saddr field of struct ipv6_pinfo
fetch = append(fetch, fmt.Sprintf("+16(+%d({{.RET}})):u64 +24(+%d({{.RET}})):u64", off, off))
}
probes = append(probes, helper.ProbeDef{
probes = append(probes, kprobes.ProbeDef{
Probe: tracing.Probe{
Type: tracing.TypeKRetProbe,
Name: "inet_sock_ipv6_guess2",
Expand All @@ -225,7 +226,7 @@ func (g *guessInetSockIPv6) Probes() (probes []helper.ProbeDef, err error) {
// Unlike with IPv4, it's not possible to bind to a random IP in the IPv6
// loopback as it is ::1/128 by default. Thus it's necessary to add temporary
// random addresses in the fd00::/8 reserved network to the loopback device.
func (g *guessInetSockIPv6) Prepare(ctx Context) (err error) {
func (g *guessInetSockIPv6) Prepare(ctx kprobes.GuessContext) (err error) {
g.ctx = ctx
g.offsets, err = getListField(g.ctx.Vars, "INET_SOCK_RADDR_LIST")
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions x-pack/auditbeat/module/system/socket/guess/inetsockaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"golang.org/x/sys/unix"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

/*
Expand Down Expand Up @@ -51,7 +51,7 @@ func init() {
}

type guessInetSockFamily struct {
ctx Context
ctx kprobes.GuessContext
family int
limit int
canIPv6 bool
Expand Down Expand Up @@ -81,21 +81,21 @@ func (g *guessInetSockFamily) Requires() []string {
// Probes returns a kprobe on inet_release which has a struct socket* as
// single argument. Returns a dump of the (struct socket*)->sk field, which is
// a struct inet_sock* for INET/INET6.
func (g *guessInetSockFamily) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
func (g *guessInetSockFamily) Probes() ([]kprobes.ProbeDef, error) {
return []kprobes.ProbeDef{
{
Probe: tracing.Probe{
Name: "inet_sock_af_guess",
Address: "inet_release",
Fetchargs: helper.MakeMemoryDump("+{{.SOCKET_SOCK}}({{.P1}})", 0, inetSockAfDumpSize),
Fetchargs: kprobes.MakeMemoryDump("+{{.SOCKET_SOCK}}({{.P1}})", 0, inetSockAfDumpSize),
},
Decoder: tracing.NewDumpDecoder,
},
}, nil
}

// Prepare is a no-op.
func (g *guessInetSockFamily) Prepare(ctx Context) error {
func (g *guessInetSockFamily) Prepare(ctx kprobes.GuessContext) error {
g.ctx = ctx
var ok bool
// limit is used as a reference point within struct sock_common to know where
Expand Down
16 changes: 8 additions & 8 deletions x-pack/auditbeat/module/system/socket/guess/iplocalout.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"golang.org/x/sys/unix"

"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/x-pack/auditbeat/module/system/socket/helper"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing"
"github.com/elastic/beats/v7/x-pack/auditbeat/tracing/kprobes"
)

// Guess how to get a struct sock* from an ip_local_out() call.
Expand Down Expand Up @@ -54,7 +54,7 @@ type skbuffSockGuess struct {
}

type guessIPLocalOut struct {
ctx Context
ctx kprobes.GuessContext
cs inetClientServer
sock uintptr
}
Expand Down Expand Up @@ -92,30 +92,30 @@ func (g *guessIPLocalOut) Requires() []string {
// * arg2 (arg1 if this system has ip_local_out_sk)
// * dump of arg1 (arg2 if this system has ip_local_out_sk)
// - tcp_sendmsg, returning the sock* argument.
func (g *guessIPLocalOut) Probes() ([]helper.ProbeDef, error) {
return []helper.ProbeDef{
func (g *guessIPLocalOut) Probes() ([]kprobes.ProbeDef, error) {
return []kprobes.ProbeDef{
{
Probe: tracing.Probe{
Name: "ip_local_out_sock_guess",
Address: "{{.IP_LOCAL_OUT}}",
Fetchargs: "arg={{if eq .IP_LOCAL_OUT \"ip_local_out\"}}{{.P2}}{{else}}{{.P1}}{{end}} dump=" +
helper.MakeMemoryDump("{{if eq .IP_LOCAL_OUT \"ip_local_out\"}}{{.P1}}{{else}}{{.P2}}{{end}}", 0, skbuffDumpSize),
kprobes.MakeMemoryDump("{{if eq .IP_LOCAL_OUT \"ip_local_out\"}}{{.P1}}{{else}}{{.P2}}{{end}}", 0, skbuffDumpSize),
},
Decoder: helper.NewStructDecoder(func() interface{} { return new(skbuffSockGuess) }),
Decoder: kprobes.NewStructDecoder(func() interface{} { return new(skbuffSockGuess) }),
},
{
Probe: tracing.Probe{
Name: "tcp_sendmsg_in",
Address: "tcp_sendmsg",
Fetchargs: "sock={{.TCP_SENDMSG_SOCK}}",
},
Decoder: helper.NewStructDecoder(func() interface{} { return new(sockArgumentGuess) }),
Decoder: kprobes.NewStructDecoder(func() interface{} { return new(sockArgumentGuess) }),
},
}, nil
}

// Prepare sets up a connected TCP client/server.
func (g *guessIPLocalOut) Prepare(ctx Context) error {
func (g *guessIPLocalOut) Prepare(ctx kprobes.GuessContext) error {
g.ctx = ctx
return g.cs.SetupTCP()
}
Expand Down
Loading

0 comments on commit 369cf01

Please sign in to comment.