Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tetragon: Add bpf program loader tests #244

Merged
merged 6 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions pkg/observer/observer_test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ func readConfig(file string) (*yaml.GenericTracingConf, error) {
return cnf, nil
}

func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, error) {
var sens []*sensors.Sensor
func getDefaultObserverSensors(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, []*sensors.Sensor, error) {
var sens, ret []*sensors.Sensor

testutils.CaptureLog(t, logger.GetLogger().(*logrus.Logger))

Expand All @@ -280,17 +280,17 @@ func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor,
var err error
sens, err = sensors.GetSensorsFromParserPolicy(&cnf.Spec)
if err != nil {
return nil, err
return nil, ret, err
}
}

if err := loadObserver(t, ctx, base, sens, o.observer.notestfail); err != nil {
return nil, err
return nil, ret, err
}

exportFname, err := testutils.GetExportFilename(t)
if err != nil {
return nil, err
return nil, ret, err
}
saveInitInfo(o, exportFname)

Expand All @@ -311,9 +311,16 @@ func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor,
testDone(t, obs)
})

ret = append(sens, base)

obs.perfConfig = bpf.DefaultPerfEventConfig()
obs.perfConfig.MapName = filepath.Join(bpf.MapPrefixPath(), "tcpmon_map")
return obs, nil
return obs, ret, nil
}

func getDefaultObserver(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, error) {
obs, _, err := getDefaultObserverSensors(t, ctx, base, opts...)
return obs, err
}

func GetDefaultObserverWithWatchers(t *testing.T, ctx context.Context, base *sensors.Sensor, opts ...TestOption) (*Observer, error) {
Expand All @@ -339,6 +346,12 @@ func GetDefaultObserverWithFile(t *testing.T, ctx context.Context, file, lib str
return GetDefaultObserverWithWatchers(t, ctx, b, WithConfig(file), withPretty(), WithLib(lib))
}

func GetDefaultSensorsWithFile(t *testing.T, ctx context.Context, file, lib string) ([]*sensors.Sensor, error) {
b := base.GetInitialSensor()
_, sens, err := getDefaultObserverSensors(t, ctx, b, WithConfig(file), withPretty(), WithLib(lib))
return sens, err
}

func GetDefaultObserverWithFileNoTest(t *testing.T, ctx context.Context, file, lib string, fail bool) (*Observer, error) {
b := base.GetInitialSensor()
return GetDefaultObserverWithWatchers(t, ctx, b, WithConfig(file), withPretty(), WithLib(lib), withNotestfail(fail))
Expand Down
40 changes: 40 additions & 0 deletions pkg/sensors/exec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ import (
"sync"
"testing"

"github.com/cilium/ebpf"
ec "github.com/cilium/tetragon/api/v1/tetragon/codegen/eventchecker"
api "github.com/cilium/tetragon/pkg/api/processapi"
"github.com/cilium/tetragon/pkg/bpf"
"github.com/cilium/tetragon/pkg/jsonchecker"
"github.com/cilium/tetragon/pkg/kernels"
sm "github.com/cilium/tetragon/pkg/matchers/stringmatcher"
"github.com/cilium/tetragon/pkg/observer"
"github.com/cilium/tetragon/pkg/option"
"github.com/cilium/tetragon/pkg/reader/namespace"
"github.com/cilium/tetragon/pkg/sensors"
"github.com/cilium/tetragon/pkg/sensors/base"
"github.com/cilium/tetragon/pkg/sensors/exec/procevents"
"github.com/cilium/tetragon/pkg/testutils"
tus "github.com/cilium/tetragon/pkg/testutils/sensors"
Expand Down Expand Up @@ -409,3 +414,38 @@ func TestEventExecveLongPathLongArgs(t *testing.T) {
err = jsonchecker.JsonTestCheck(t, checker)
assert.NoError(t, err)
}

func TestLoadInitialSensor(t *testing.T) {

var sensorProgs = []tus.SensorProg{
0: tus.SensorProg{Name: "event_execve", Type: ebpf.TracePoint},
1: tus.SensorProg{Name: "event_exit", Type: ebpf.TracePoint},
2: tus.SensorProg{Name: "event_wake_up_new_task", Type: ebpf.Kprobe},
}

var sensorMaps = []tus.SensorMap{
// all programs
tus.SensorMap{Name: "execve_map", Progs: []uint{0, 1, 2}},
tus.SensorMap{Name: "execve_map_stats", Progs: []uint{0, 1, 2}},
tus.SensorMap{Name: "tcpmon_map", Progs: []uint{0, 1, 2}},

// event_execve
tus.SensorMap{Name: "names_map", Progs: []uint{0}},

// event_wake_up_new_task
tus.SensorMap{Name: "execve_val", Progs: []uint{2}},
}

sensor := base.GetInitialSensor()

option.Config.HubbleLib = tus.Conf().TetragonLib

t.Logf("Loading sensor %v\n", sensor.Name)
if err := sensor.Load(context.TODO(), bpf.MapPrefixPath(), bpf.MapPrefixPath(), ""); err != nil {
t.Fatalf("sensor.Load failed: %v\n", err)
}

tus.CheckSensorLoad([]*sensors.Sensor{sensor}, sensorMaps, sensorProgs, t)

sensors.UnloadAll(tus.Conf().TetragonLib)
}
170 changes: 170 additions & 0 deletions pkg/sensors/program/coll.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

package program

import (
"errors"
"fmt"
"os"

"github.com/cilium/ebpf"
"github.com/cilium/tetragon/pkg/logger"
)

// The idea of LoadedCollection is to keep loaded programs and maps
// from ebpf.Collection to be used later for 'TestLoad*' tests to
// verify loaded programs and maps.
//
// We can't just iterate all existing programs and maps for 2 reasons:
// - we could race with others loading bpf programs
// - we get limited name (16 bytes), which we usually cross with
// our names for maps and programs
//
// The process of loading LoadedCollection is following:
//
// - ebpf collection is loaded
//
// coll := NewCollectionWithOptions
//
// - copy all programs/maps from ebpf collection
//
// copyLoadedCollection(lc, coll)
//
// - collection is removed and only 'used' programs and maps stay loaded
// so we filter out unused programs/maps with (based on their IDs)
//
// load.LC = filterLoadedCollection(lc)
//
// This way we have only programs/maps realted to our test and with
// original long names.
//
kkourt marked this conversation as resolved.
Show resolved Hide resolved
// All this is happening only when program.KeepCollection is set true,
// so it's enabled only for testing code.

var (
KeepCollection bool
)

type LoadedMap struct {
ID ebpf.MapID
}

type LoadedProgram struct {
ID ebpf.ProgramID
MapIDs []ebpf.MapID
Type ebpf.ProgramType
}

type LoadedCollection struct {
Programs map[string]*LoadedProgram
Maps map[string]*LoadedMap
}

func newLoadedCollection() *LoadedCollection {
lc := &LoadedCollection{}
lc.Maps = map[string]*LoadedMap{}
lc.Programs = map[string]*LoadedProgram{}
return lc
}

func printLoadedCollection(str string, lc *LoadedCollection) {
logger.GetLogger().Infof("Programs (%s):", str)
for name, lp := range lc.Programs {
logger.GetLogger().Infof("%d: %s - %v", lp.ID, name, lp.MapIDs)
}
logger.GetLogger().Infof("Maps (%s):", str)
for name, lm := range lc.Maps {
logger.GetLogger().Infof("%d: %s", lm.ID, name)
}
}

func copyLoadedCollection(coll *ebpf.Collection) (*LoadedCollection, error) {
if coll == nil {
return nil, fmt.Errorf("failed to get collection")
}
lc := newLoadedCollection()
// copy all loaded maps
for name, m := range coll.Maps {
info, err := m.Info()
if err != nil {
return nil, err
}
id, ok := info.ID()
if !ok {
return nil, fmt.Errorf("failed to get id")
}
lm := &LoadedMap{id}
lc.Maps[name] = lm
}
// copy all loaded programs with assigned map ids
for name, p := range coll.Programs {
info, err := p.Info()
if err != nil {
return nil, err
}
id, ok := info.ID()
if !ok {
return nil, fmt.Errorf("failed to get id")
}
mapIDs, ok := info.MapIDs()
if !ok {
return nil, fmt.Errorf("failed to get map ids")
}
lp := &LoadedProgram{ID: id}
lp.MapIDs = mapIDs
lp.Type = p.Type()
lc.Programs[name] = lp
}
return lc, nil
}

// Gets all the programs/maps and removes any non existent ones
// from passed collection
func filterLoadedCollection(lc *LoadedCollection) *LoadedCollection {
kkourt marked this conversation as resolved.
Show resolved Hide resolved
ret := newLoadedCollection()

// filter out non existing programs
lastProg := ebpf.ProgramID(0)
for {
next, err := ebpf.ProgramGetNextID(lastProg)
if errors.Is(err, os.ErrNotExist) {
break
}
for name, lp := range lc.Programs {
if lp.ID == next {
ret.Programs[name] = lp
}
}
lastProg = next
}

// filter out non existing maps
lastMap := ebpf.MapID(0)
for {
next, err := ebpf.MapGetNextID(lastMap)
if errors.Is(err, os.ErrNotExist) {
break
}
for name, lm := range lc.Maps {
if lm.ID == next {
ret.Maps[name] = lm
}
}
lastMap = next
}
for _, lp := range lc.Programs {
var mapIDs []ebpf.MapID

for _, mi := range lp.MapIDs {
for _, lm := range lc.Maps {
if lm.ID == mi {
mapIDs = append(mapIDs, mi)
break
}
}
}
lp.MapIDs = mapIDs
}
return ret
}
Loading