-
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.
loggerCallback() calls callbacks, log() and []logFnFilters(), which can 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. This also introduces LogWarnLevel, LogInfoLevel and LogDebugLevel constants.
- Loading branch information
Showing
2 changed files
with
170 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,157 @@ | ||
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 | ||
|
||
// 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) | ||
} | ||
|
||
const ( | ||
LogWarnLevel = int(C.LIBBPF_WARN) | ||
LogInfoLevel = int(C.LIBBPF_INFO) | ||
LogDebugLevel = int(C.LIBBPF_DEBUG) | ||
) | ||
|
||
var log = logFallback | ||
var logFnFilters = []func(libLevel int, msg string) bool{ | ||
LogFilterLevel, | ||
LogFilterOutput, | ||
} | ||
|
||
// SetLoggerCbs receives a function to be used to log libbpf outputs and | ||
// a list of functions to filter this output out | ||
// If fnLog is nil, logFallback will continue to be used | ||
// If fnFilters is nil, logFnFilters will continue to be used | ||
func SetLoggerCbs( | ||
fnLog func(level int, msg string, keyValues ...interface{}), // log callback | ||
fnFilters []func(libLevel int, msg string) bool, // Filter out callbacks | ||
) { | ||
if fnLog != nil { | ||
log = fnLog | ||
} | ||
if fnFilters != nil { | ||
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 { | ||
outMsg += " [%s]" | ||
args = append(args, s) | ||
} | ||
} | ||
|
||
outMsg += "\n" | ||
if len(keyValues) > 0 { | ||
fmt.Fprintf(os.Stderr, outMsg, args) | ||
} else { | ||
fmt.Fprint(os.Stderr, outMsg) | ||
} | ||
} | ||
|
||
// LogFilterLevel filters by checking its print level | ||
// In case the consumer defines its own filters functions via SetLoggerCbs, this can also be passed | ||
func LogFilterLevel(libbpfPrintLevel int, output string) bool { | ||
return libbpfPrintLevel != LogWarnLevel | ||
} | ||
|
||
// LogFilterOutput filters out some errors by using regex | ||
// In case the consumer defines its own filters functions via SetLoggerCbs, this can also be passed | ||
func LogFilterOutput(libbpfPrintLevel int, output string) bool { | ||
var ( | ||
matched bool | ||
err error | ||
) | ||
|
||
// 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(LogDebugLevel, "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(LogDebugLevel, "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(LogDebugLevel, "Filtering libbpf print output", "error", err) | ||
} else if matched { | ||
return true | ||
} | ||
|
||
return false | ||
} |