-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
logger: introduce tracee logger callbacks
loggerCallback() calls log and filter callbacks, Log and logFnFilters, which must be set by the libbpfgo consumer via SetLoggerCbs(). This moves output filtering from C libbpf_print_fn() to Go filterOutput() which can be set as one of the slice of filter funcs.
- Loading branch information
Showing
2 changed files
with
159 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package libbpfgo | ||
|
||
/* | ||
#include <bpf/libbpf.h> | ||
*/ | ||
import "C" | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
// This callback definition needs to be in a different file from where it is declared in C | ||
// Otherwise, multiple definition compilation error will occur | ||
|
||
func init() { | ||
if Log == nil { | ||
Log = logFallback | ||
} | ||
} | ||
|
||
// loggerCallback is called by libbpf_print_fn() which in turn is called by libbpf | ||
// | ||
//export loggerCallback | ||
func loggerCallback(libbpfPrintLevel int, libbpfOutput *C.char) { | ||
var ( | ||
level int | ||
goOutput string | ||
) | ||
|
||
goOutput = C.GoString(libbpfOutput) | ||
goOutput = strings.TrimSuffix(goOutput, "\n") | ||
|
||
for _, fnFilterOut := range logFnFilters { | ||
if fnFilterOut != nil { | ||
if fnFilterOut(libbpfPrintLevel, goOutput) { | ||
return | ||
} | ||
} | ||
} | ||
|
||
Log(level, goOutput) | ||
} | ||
|
||
var Log func(level int, msg string, keyValues ...interface{}) | ||
var logFnFilters []func(libLevel int, msg string) bool | ||
|
||
// SetLoggerCbs receives a function to be used to log libbpf outputs and | ||
// a list of functions to filter this output out | ||
func SetLoggerCbs( | ||
fnLog func(level int, msg string, keyValues ...interface{}), // Log callback | ||
fnFilters []func(libLevel int, msg string) bool, // Filter out callbacks | ||
) { | ||
Log = fnLog | ||
logFnFilters = fnFilters | ||
} | ||
|
||
// logFallback: | ||
// - level is ignored in this stage | ||
// - type coercion only takes care of string types | ||
// - keyValues is not required to contain pairs | ||
// - outputs all to stderr | ||
func logFallback(level int, msg string, keyValues ...interface{}) { | ||
var ( | ||
args = make([]string, 0) | ||
outMsg = msg | ||
) | ||
|
||
for _, v := range keyValues { | ||
if s, ok := v.(string); ok { | ||
fmt.Println("string:", s) | ||
outMsg += " [%s]" | ||
args = append(args, s) | ||
} | ||
} | ||
|
||
outMsg += "\n" | ||
if len(keyValues) > 0 { | ||
fmt.Fprintf(os.Stderr, outMsg, args) | ||
} else { | ||
fmt.Fprint(os.Stderr, outMsg) | ||
} | ||
} | ||
|
||
// LogFilterOutput filters some errors out | ||
// The consumer can use it by passing it as a filter function via SetLoggerCbs | ||
func LogFilterOutput(libbpfPrintLevel int, output string) bool { | ||
var ( | ||
matched bool | ||
debugLevel int | ||
err error | ||
) | ||
|
||
debugLevel = int(C.LIBBPF_DEBUG) | ||
|
||
// BUG: https:/github.com/aquasecurity/tracee/issues/1676 | ||
|
||
// triggered by: libbpf/src/nlattr.c->libbpf_nla_dump_errormsg() | ||
// "libbpf: Kernel error message: %s\n" | ||
// 1. %s = "Exclusivity flag on" | ||
|
||
matched, err = regexp.MatchString(`libbpf:.*Kernel error message:.*Exclusivity flag on`, output) | ||
if err != nil { | ||
Log(debugLevel, "Filtering libbpf print output", "error", err) | ||
} else if matched { | ||
return true | ||
} | ||
|
||
// BUG: https://github.com/aquasecurity/tracee/issues/2446 | ||
|
||
// triggered by: libbpf/src/libbpf.c->bpf_program__attach_kprobe_opts() | ||
// "libbpf: prog '%s': failed to create %s '%s+0x%zx' perf event: %s\n" | ||
// 1. %s = trace_check_map_func_compatibility | ||
// 2. %s = kretprobe or kprobe | ||
// 3. %s = check_map_func_compatibility (function name) | ||
// 4. %x = offset (ignored in this check) | ||
// 5. %s = No such file or directory | ||
|
||
matched, err = regexp.MatchString(`libbpf:.*prog 'trace_check_map_func_compatibility'.*failed to create kprobe.*perf event: No such file or directory`, output) | ||
if err != nil { | ||
Log(debugLevel, "Filtering libbpf print output", "error", err) | ||
} else if matched { | ||
return true | ||
} | ||
|
||
// AttachCgroupLegacy() will first try AttachCgroup() and it might fail. This | ||
// is not an error and is the best way of probing for eBPF cgroup attachment | ||
// link existence. | ||
|
||
// triggered by: libbpf/src/libbpf.c->bpf_program__attach_fd() | ||
// "libbpf: prog '%s': failed to attach to %s: %s\n" | ||
// 1. %s = cgroup_skb_ingress or cgroup_skb_egress | ||
// 2. %s = cgroup | ||
// 3. %s = Invalid argument | ||
|
||
matched, err = regexp.MatchString(`libbpf:.*prog 'cgroup_skb_ingress|cgroup_skb_egress'.*failed to attach to cgroup.*Invalid argument`, output) | ||
if err != nil { | ||
Log(debugLevel, "Filtering libbpf print output", "error", err) | ||
} else if matched { | ||
return true | ||
} | ||
|
||
return false | ||
} |