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: Refactor program/map loader to use directory hierarchy #2128

Merged
merged 21 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
72ee92e
tetragon: Add PinPath to Map object
olsajiri Jun 16, 2024
7750be4
tetragon: Use map pin path for MapLoad interface
olsajiri Feb 24, 2024
8796e47
tetragon: Add shared map type fields
olsajiri Feb 21, 2024
d76c5d2
tetragon: Create sensor directory hierarchy
olsajiri Feb 23, 2024
e80c9c9
tetragon: Move base sensor maps under new hierarchy
olsajiri Feb 23, 2024
d7a30b3
tetragon: Move generickprobe sensor maps under new hierarchy
olsajiri Feb 23, 2024
08dfece
tetragon: Move generictracepoint sensor maps under new hierarchy
olsajiri Feb 25, 2024
31bbcc0
tetragon: Move genericuprobe sensor maps under new hierarchy
olsajiri Feb 25, 2024
75269c7
tetragon: Move genericlsm sensor maps under new hierarchy
olsajiri Jul 19, 2024
06f15fd
tetragon: Change generickprobe sensor pin path
olsajiri Feb 24, 2024
367c645
tetragon: Change generictracepoint sensor pin path
olsajiri Feb 25, 2024
edd45bb
tetragon: Change genericuprobe sensor pin path
olsajiri Feb 25, 2024
951c90a
tetragon: Change genericlsm sensor pin path
olsajiri Jul 19, 2024
b9fa81b
tetragon: Move enforcer sensor maps under new hierarchy
olsajiri Feb 25, 2024
8716b8a
tetragon: Get rid of MapBuilderPin functions
olsajiri Jun 16, 2024
c1f2185
tetragon: Sanitize policy name before using it in path
olsajiri Jun 13, 2024
4609398
tetragon: Adjust linkPinPath for new hierarchy
olsajiri Jul 9, 2024
1a69192
tetragon: Add policy argument to SensorBuilder function
olsajiri May 24, 2024
8f102dc
tetragon: Add tests for map builders
olsajiri Jun 18, 2024
76544da
tetragon: Add tests for map max entries setup
olsajiri Jun 18, 2024
d0da915
tetragon: Add documentation for maps usage
olsajiri Jun 18, 2024
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
3 changes: 2 additions & 1 deletion bpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ PROCESS = bpf_execve_event.o bpf_execve_event_v53.o bpf_fork.o bpf_exit.o bpf_ge
bpf_generic_lsm_v61.o \
bpf_loader.o \
bpf_cgroup.o \
bpf_enforcer.o bpf_multi_enforcer.o bpf_fmodret_enforcer.o
bpf_enforcer.o bpf_multi_enforcer.o bpf_fmodret_enforcer.o \
bpf_map_test_p1.o bpf_map_test_p2.o

CGROUP = bpf_cgroup_mkdir.o bpf_cgroup_rmdir.o bpf_cgroup_release.o
BPFTEST = bpf_lseek.o
Expand Down
30 changes: 30 additions & 0 deletions bpf/process/bpf_map_test_p1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright Authors of Cilium */

#include "vmlinux.h"
#include "api.h"
#include "bpf_tracing.h"
#include "bpf_helpers.h"
#include "compiler.h"

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} m1 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} m2 SEC(".maps");

__attribute__((section("kprobe/wake_up_new_task"), used)) int
BPF_KPROBE(p1)
{
map_lookup_elem(&m1, &(int){ 0 });
map_lookup_elem(&m2, &(int){ 0 });
return 0;
}
30 changes: 30 additions & 0 deletions bpf/process/bpf_map_test_p2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright Authors of Cilium */

#include "vmlinux.h"
#include "api.h"
#include "bpf_tracing.h"
#include "bpf_helpers.h"
#include "compiler.h"

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} m1 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} m2 SEC(".maps");

__attribute__((section("kprobe/wake_up_new_task"), used)) int
BPF_KPROBE(p2)
{
map_lookup_elem(&m1, &(int){ 0 });
map_lookup_elem(&m2, &(int){ 0 });
return 0;
}
2 changes: 1 addition & 1 deletion pkg/sensors/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ var (
TCPMonMap = program.MapBuilder("tcpmon_map", Execve)
/* Networking and Process Monitoring maps */
ExecveMap = program.MapBuilder("execve_map", Execve)
ExecveTailCallsMap = program.MapBuilderPin("execve_calls", "execve_calls", Execve)
ExecveTailCallsMap = program.MapBuilderProgram("execve_calls", Execve)

ExecveJoinMap = program.MapBuilder("tg_execve_joined_info_map", ExecveBprmCommit)

Expand Down
28 changes: 26 additions & 2 deletions pkg/sensors/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ func LoadConfig(bpfDir string, sens []*Sensor) error {
return nil
}

func (s *Sensor) setupProgsPinPath(bpfDir string) {
for _, p := range s.Progs {
// setup sensor based program pin path
p.PinPath = filepath.Join(sanitize(s.Policy), s.Name, p.PinName)
// and make the path
os.MkdirAll(filepath.Join(bpfDir, p.PinPath), os.ModeDir)
}
}

// Load loads the sensor, by loading all the BPF programs and maps.
func (s *Sensor) Load(bpfDir string) error {
if s == nil {
Expand All @@ -77,7 +86,7 @@ func (s *Sensor) Load(bpfDir string) error {
return fmt.Errorf("tetragon, aborting minimum requirements not met: %w", err)
}

os.Mkdir(bpfDir, os.ModeDir)
s.setupProgsPinPath(bpfDir)

l := logger.GetLogger()

Expand Down Expand Up @@ -209,6 +218,20 @@ func (s *Sensor) FindPrograms() error {
return nil
}

func (s *Sensor) setMapPinPath(m *program.Map) {
policy := sanitize(s.Policy)
switch m.Type {
case program.MapTypeGlobal:
m.PinPath = filepath.Join(m.Name)
case program.MapTypePolicy:
m.PinPath = filepath.Join(policy, m.Name)
case program.MapTypeSensor:
m.PinPath = filepath.Join(policy, s.Name, m.Name)
case program.MapTypeProgram:
m.PinPath = filepath.Join(policy, s.Name, m.Prog.PinName, m.Name)
}
}

// loadMaps loads all the BPF maps in the sensor.
func (s *Sensor) loadMaps(bpfDir string) error {
l := logger.GetLogger()
Expand All @@ -222,7 +245,8 @@ func (s *Sensor) loadMaps(bpfDir string) error {
continue
}

pinPath := filepath.Join(bpfDir, m.PinName)
s.setMapPinPath(m)
pinPath := filepath.Join(bpfDir, m.PinPath)

spec, err := ebpf.LoadCollectionSpec(m.Prog.Name)
if err != nil {
Expand Down
26 changes: 12 additions & 14 deletions pkg/sensors/program/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,11 @@ type LoadOpts struct {
}

func linkPinPath(bpfDir string, load *Program, extra ...string) string {
pinPath := filepath.Join(bpfDir, load.PinPath)
if load.Override {
pinPath = pinPath + "_override"
}
if load.RetProbe {
pinPath = pinPath + "_return"
}
pinPath := filepath.Join(bpfDir, load.PinPath, "link")
if len(extra) != 0 {
pinPath = pinPath + "_" + strings.Join(extra, "_")
}
return pinPath + "_link"
return pinPath
}

func linkPin(lnk link.Link, bpfDir string, load *Program, extra ...string) error {
Expand Down Expand Up @@ -229,13 +223,13 @@ func kprobeAttachOverride(load *Program, bpfDir string,
return fmt.Errorf("failed to clone generic_kprobe_override program: %w", err)
}

pinPath := filepath.Join(bpfDir, fmt.Sprint(load.PinPath, "-override"))
pinPath := filepath.Join(bpfDir, load.PinPath, "prog_override")

if err := prog.Pin(pinPath); err != nil {
return fmt.Errorf("pinning '%s' to '%s' failed: %w", load.Label, pinPath, err)
}

load.unloaderOverride, err = kprobeAttach(load, prog, spec, load.Attach, bpfDir)
load.unloaderOverride, err = kprobeAttach(load, prog, spec, load.Attach, bpfDir, "override")
if err != nil {
logger.GetLogger().Warnf("Failed to attach override program: %w", err)
}
Expand Down Expand Up @@ -732,7 +726,7 @@ func installTailCalls(bpfDir string, spec *ebpf.CollectionSpec, coll *ebpf.Colle
}

if load.TcMap != nil {
if err := install(load.TcMap.PinName, load.TcPrefix); err != nil {
if err := install(load.TcMap.PinPath, load.TcPrefix); err != nil {
return err
}
}
Expand Down Expand Up @@ -817,7 +811,7 @@ func doLoadProgram(
var err error
var mapPath string
if pm, ok := load.PinMap[name]; ok {
mapPath = filepath.Join(bpfDir, pm.PinName)
mapPath = filepath.Join(bpfDir, pm.PinPath)
} else {
mapPath = filepath.Join(bpfDir, name)
}
Expand Down Expand Up @@ -869,8 +863,12 @@ func doLoadProgram(
}

for _, mapLoad := range load.MapLoad {
pinPath := ""
if pm, ok := load.PinMap[mapLoad.Name]; ok {
pinPath = pm.PinPath
}
if m, ok := coll.Maps[mapLoad.Name]; ok {
if err := mapLoad.Load(m, mapLoad.Index); err != nil {
if err := mapLoad.Load(m, pinPath, mapLoad.Index); err != nil {
return nil, err
}
} else {
Expand All @@ -883,7 +881,7 @@ func doLoadProgram(
return nil, fmt.Errorf("program for section '%s' not found", load.Label)
}

pinPath := filepath.Join(bpfDir, load.PinPath)
pinPath := filepath.Join(bpfDir, load.PinPath, "prog")

if _, err := os.Stat(pinPath); err == nil {
logger.GetLogger().Debugf("Pin file '%s' already exists, repinning", load.PinPath)
Expand Down
19 changes: 10 additions & 9 deletions pkg/sensors/program/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ func TestLoaderLinkPinPath(t *testing.T) {
var load *Program
var pin string

// standard link
load = Builder("", "", "", "event", "")
load.PinPath = "test/generic_kprobe/__x64_sys_linkat"
pin = linkPinPath(bpfDir, load)
assert.Equal(t, filepath.Join(bpfDir, "event_link"), pin)
assert.Equal(t, filepath.Join(bpfDir, "test/generic_kprobe/__x64_sys_linkat", "link"), pin)

// override link
load = Builder("", "", "", "event", "")
load.Override = true
pin = linkPinPath(bpfDir, load)
assert.Equal(t, filepath.Join(bpfDir, "event_override_link"), pin)

load = Builder("", "", "", "event", "").SetRetProbe(true)
pin = linkPinPath(bpfDir, load)
assert.Equal(t, filepath.Join(bpfDir, "event_return_link"), pin)
load.PinPath = "test/generic_kprobe/__x64_sys_linkat"
pin = linkPinPath(bpfDir, load, "override")
assert.Equal(t, filepath.Join(bpfDir, "test/generic_kprobe/__x64_sys_linkat", "link_override"), pin)

// many-kprobe link
load = Builder("", "", "", "event", "")
load.PinPath = "test/generic_kprobe/__x64_sys_linkat"
pin = linkPinPath(bpfDir, load, "1_sys_exit")
assert.Equal(t, filepath.Join(bpfDir, "event_1_sys_exit_link"), pin)
assert.Equal(t, filepath.Join(bpfDir, "test/generic_kprobe/__x64_sys_linkat", "link_1_sys_exit"), pin)
}
87 changes: 80 additions & 7 deletions pkg/sensors/program/map.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,53 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

// We allow to define several types of maps:
//
// MapTypeGlobal MapType = iota
// MapTypePolicy
// MapTypeSensor
// MapTypeProgram
//
// Each type defines the maps position in the sysfs hierarchy:
//
// MapTypeGlobal: /sys/fs/bpf/tetragon/map
// MapTypePolicy: /sys/fs/bpf/tetragon/policy/map
// MapTypeSensor: /sys/fs/bpf/tetragon/policy/sensor/map
// MapTypeProgram: /sys/fs/bpf/tetragon/policy/sensor/program/map
//
// Each type has appropriate helper defined, which sets map's
// path to specific level of sysfs hierarchy:
//
// MapTypeGlobal: MapBuilder
// MapTypePolicy: MapBuilderPolicy
// MapTypeSensor: MapBuilderSensor
// MapTypeProgram: MapBuilderProgram
//
// It's possible to share map between more programs like:
//
// m := MapBuilderSensor("map", prog1, prog2, prog3)
//
// All prog1-3 programs will attach to m1 through:
//
// /sys/fs/bpf/tetragon/policy/sensor/map
//
// The idea is to share map on higher level which denotes to scope
// of the map, like:
//
// /sys/fs/bpf/tetragon/map
// - map is global shared with all policies/sensors/programs
//
// /sys/fs/bpf/tetragon/policy/map
// - map is local for policy, shared by all its sensors/programs
//
// /sys/fs/bpf/tetragon/policy/sensors/map
// - map is local for sensor, shared by all its programs
//
// /sys/fs/bpf/tetragon/policy/sensors/program/map
// - map is local for program, not shared at all
//
// NOTE Please do not share MapTypeProgram maps, it brings confusion.

package program

import (
Expand All @@ -19,15 +66,25 @@ type MaxEntries struct {
Set bool
}

type MapType int

const (
MapTypeGlobal MapType = iota
MapTypePolicy
MapTypeSensor
MapTypeProgram
)

// Map represents BPF maps.
type Map struct {
Name string
PinName string
PinPath string
Prog *Program
PinState State
MapHandle *ebpf.Map
Entries MaxEntries
InnerEntries MaxEntries
Type MapType
}

// Map holds pointer to Program object as a source of its ebpf object
Expand All @@ -43,24 +100,40 @@ type Map struct {
// p.PinMap["map2"] = &map2
// ...
// p.PinMap["mapX"] = &mapX
func mapBuilder(name, pin string, lds ...*Program) *Map {
m := &Map{name, pin, lds[0], Idle(), nil, MaxEntries{0, false}, MaxEntries{0, false}}
func mapBuilder(name string, ty MapType, lds ...*Program) *Map {
m := &Map{name, "", lds[0], Idle(), nil, MaxEntries{0, false}, MaxEntries{0, false}, ty}
for _, ld := range lds {
ld.PinMap[name] = m
}
return m
}

func MapBuilder(name string, lds ...*Program) *Map {
return mapBuilder(name, name, lds...)
return mapBuilder(name, MapTypeGlobal, lds...)
}

func MapBuilderProgram(name string, lds ...*Program) *Map {
return mapBuilder(name, MapTypeProgram, lds...)
}

func MapBuilderSensor(name string, lds ...*Program) *Map {
return mapBuilder(name, MapTypeSensor, lds...)
}

func MapBuilderPolicy(name string, lds ...*Program) *Map {
return mapBuilder(name, MapTypePolicy, lds...)
}

func MapBuilderType(name string, ty MapType, lds ...*Program) *Map {
return mapBuilder(name, ty, lds...)
}

func MapBuilderPin(name, pin string, lds ...*Program) *Map {
return mapBuilder(name, pin, lds...)
func PolicyMapPath(mapDir, policy, name string) string {
return filepath.Join(mapDir, policy, name)
}

func (m *Map) Unload() error {
log := logger.GetLogger().WithField("map", m.Name).WithField("pin", m.PinName)
log := logger.GetLogger().WithField("map", m.Name).WithField("pin", m.Name)
if !m.PinState.IsLoaded() {
log.WithField("count", m.PinState.count).Debug("Refusing to unload map as it is not loaded")
return nil
Expand Down
Loading
Loading