From b00d497f01cc7113fd5417702784699b7f5c729f Mon Sep 17 00:00:00 2001 From: Anadi Sharma Date: Wed, 2 Apr 2025 17:20:48 -0400 Subject: [PATCH] tetragon/windows: Support Windows create and exit process - sensor changes This PR lists sensor side changes to support CreateProcess and ExitProcess events. The program loader needs to load the entire collection of native Windows ebps program image using cilium/ebpf library. The specs for a native Windows bpf program is not available as it is not in ELF format This changes the order in which maps are loaded - collection is loaded first which loads maps and programs automatically. Signed-off-by: Anadi Anadi --- pkg/sensors/base/base.go | 27 --- pkg/sensors/base/base_linux.go | 35 ++++ pkg/sensors/base/base_windows.go | 39 +++++ pkg/sensors/config/confmap/confmap.go | 4 +- pkg/sensors/load.go | 87 ++++++++++ pkg/sensors/load_linux.go | 109 ++---------- pkg/sensors/load_windows.go | 90 ++-------- pkg/sensors/program/loader.go | 122 ++++++++++++++ pkg/sensors/program/loader_linux.go | 110 ------------- pkg/sensors/program/loader_windows.go | 229 ++++++++++++++++++++++++++ 10 files changed, 544 insertions(+), 308 deletions(-) create mode 100644 pkg/sensors/base/base_linux.go create mode 100644 pkg/sensors/base/base_windows.go create mode 100644 pkg/sensors/program/loader.go create mode 100644 pkg/sensors/program/loader_windows.go diff --git a/pkg/sensors/base/base.go b/pkg/sensors/base/base.go index 80ce370f8e6..33deb375708 100644 --- a/pkg/sensors/base/base.go +++ b/pkg/sensors/base/base.go @@ -155,33 +155,6 @@ func GetTetragonConfMap() *program.Map { return TetragonConfMap } -func GetDefaultPrograms() []*program.Program { - progs := []*program.Program{ - Exit, - Fork, - Execve, - ExecveBprmCommit, - } - return progs -} - -func GetDefaultMaps() []*program.Map { - maps := []*program.Map{ - ExecveMap, - ExecveJoinMap, - ExecveStats, - ExecveJoinMapStats, - ExecveTailCallsMap, - TCPMonMap, - TetragonConfMap, - StatsMap, - MatchBinariesSetMap, - ErrMetricsMap, - } - return maps - -} - func initBaseSensor() *sensors.Sensor { sensor := sensors.Sensor{ Name: basePolicy, diff --git a/pkg/sensors/base/base_linux.go b/pkg/sensors/base/base_linux.go new file mode 100644 index 00000000000..acffa9127fe --- /dev/null +++ b/pkg/sensors/base/base_linux.go @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package base + +import ( + "github.com/cilium/tetragon/pkg/sensors/program" +) + +func GetDefaultPrograms() []*program.Program { + progs := []*program.Program{ + Exit, + Fork, + Execve, + ExecveBprmCommit, + } + return progs +} + +func GetDefaultMaps() []*program.Map { + maps := []*program.Map{ + ExecveMap, + ExecveJoinMap, + ExecveStats, + ExecveJoinMapStats, + ExecveTailCallsMap, + TCPMonMap, + TetragonConfMap, + StatsMap, + MatchBinariesSetMap, + ErrMetricsMap, + } + return maps + +} diff --git a/pkg/sensors/base/base_windows.go b/pkg/sensors/base/base_windows.go new file mode 100644 index 00000000000..02762902393 --- /dev/null +++ b/pkg/sensors/base/base_windows.go @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package base + +import ( + "github.com/cilium/tetragon/pkg/sensors/program" +) + +var ( + CreateProcess = program.Builder( + "process_monitor.sys", + "process", + "ProcessMonitor", + "process::program", + "windows", + ).SetPolicy(basePolicy) + + ProcessRingBufMap = program.MapBuilder("process_ringbuf", CreateProcess) + ProcessPidMap = program.MapBuilder("process_map", CreateProcess) + ProcessCmdMap = program.MapBuilder("command_map", CreateProcess) +) + +func GetDefaultPrograms() []*program.Program { + progs := []*program.Program{ + CreateProcess, + } + return progs +} + +func GetDefaultMaps() []*program.Map { + maps := []*program.Map{ + ProcessRingBufMap, + ProcessCmdMap, + ProcessPidMap, + } + return maps + +} diff --git a/pkg/sensors/config/confmap/confmap.go b/pkg/sensors/config/confmap/confmap.go index 67fa9e0f3dc..455927084bf 100644 --- a/pkg/sensors/config/confmap/confmap.go +++ b/pkg/sensors/config/confmap/confmap.go @@ -11,12 +11,12 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/tetragon/pkg/cgroups" "github.com/cilium/tetragon/pkg/config" + "github.com/cilium/tetragon/pkg/constants" "github.com/cilium/tetragon/pkg/logger" "github.com/cilium/tetragon/pkg/option" "github.com/cilium/tetragon/pkg/sensors/base" "github.com/cilium/tetragon/pkg/sensors/program" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) const ( @@ -122,7 +122,7 @@ func UpdateTgRuntimeConf(mapDir string, nspid int) error { return err } - if v.CgrpFsMagic == unix.CGROUP2_SUPER_MAGIC { + if v.CgrpFsMagic == constants.CGROUP2_SUPER_MAGIC { log.WithFields(logrus.Fields{ "confmap-update": configMapName, "deployment.mode": deployMode.String(), diff --git a/pkg/sensors/load.go b/pkg/sensors/load.go index 2eafb6c6869..1b5b884dd76 100644 --- a/pkg/sensors/load.go +++ b/pkg/sensors/load.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" + "github.com/cilium/tetragon/pkg/kernels" "github.com/cilium/tetragon/pkg/logger" "github.com/cilium/tetragon/pkg/option" "github.com/cilium/tetragon/pkg/sensors/program" @@ -102,6 +103,92 @@ func (s *Sensor) removeDirs() { os.Remove(filepath.Join(s.BpfDir, s.policyDir())) } +// Load loads the sensor, by loading all the BPF programs and maps. +func (s *Sensor) Load(bpfDir string) (err error) { + if s == nil { + return nil + } + + if s.Destroyed { + return fmt.Errorf("sensor %s has been previously destroyed, please recreate it before loading", s.Name) + } + + logger.GetLogger().WithField("metadata", getCachedBTFFile()).Info("BTF file: using metadata file") + if _, err = observerMinReqs(); err != nil { + return fmt.Errorf("tetragon, aborting minimum requirements not met: %w", err) + } + + var ( + loadedMaps []*program.Map + loadedProgs []*program.Program + ) + + s.createDirs(bpfDir) + defer func() { + if err != nil { + for _, m := range loadedMaps { + m.Unload(true) + } + for _, p := range loadedProgs { + unloadProgram(p, true) + } + s.removeDirs() + } + }() + + l := logger.GetLogger() + + l.WithField("name", s.Name).Info("Loading sensor") + if s.Loaded { + return fmt.Errorf("loading sensor %s failed: sensor already loaded", s.Name) + } + + _, verStr, _ := kernels.GetKernelVersion(option.Config.KernelVersion, option.Config.ProcFS) + l.Infof("Loading kernel version %s", verStr) + + if err = s.FindPrograms(); err != nil { + return fmt.Errorf("tetragon, aborting could not find BPF programs: %w", err) + } + if loadedMaps, err = s.preLoadMaps(bpfDir, loadedMaps); err != nil { + return err + } + for _, p := range s.Progs { + if p.LoadState.IsLoaded() { + l.WithField("prog", p.Name).Info("BPF prog is already loaded, incrementing reference count") + p.LoadState.RefInc() + continue + } + + if err = observerLoadInstance(bpfDir, p, s.Maps); err != nil { + return err + } + p.LoadState.RefInc() + loadedProgs = append(loadedProgs, p) + l.WithField("prog", p.Name).WithField("label", p.Label).Debugf("BPF prog was loaded") + } + + // Add the *loaded* programs and maps, so they can be unloaded later + progsAdd(s.Progs) + AllMaps = append(AllMaps, s.Maps...) + + if s.PostLoadHook != nil { + if err := s.PostLoadHook(); err != nil { + logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Post load hook failed") + } + } + + // cleanup the BTF once we have loaded all sensor's program + flushKernelSpec() + + l.WithFields(logrus.Fields{ + "sensor": s.Name, + "maps": loadedMaps, + "progs": loadedProgs, + }).Infof("Loaded BPF maps and events for sensor successfully") + s.Loaded = true + return nil +} + func (s *Sensor) Unload(unpin bool) error { logger.GetLogger().Infof("Unloading sensor %s", s.Name) if !s.Loaded { diff --git a/pkg/sensors/load_linux.go b/pkg/sensors/load_linux.go index 59a00f46c5a..7dc4ceec6e4 100644 --- a/pkg/sensors/load_linux.go +++ b/pkg/sensors/load_linux.go @@ -17,97 +17,6 @@ import ( "github.com/sirupsen/logrus" ) -// Load loads the sensor, by loading all the BPF programs and maps. -func (s *Sensor) Load(bpfDir string) (err error) { - if s == nil { - return nil - } - - if s.Destroyed { - return fmt.Errorf("sensor %s has been previously destroyed, please recreate it before loading", s.Name) - } - - logger.GetLogger().WithField("metadata", cachedbtf.GetCachedBTFFile()).Info("BTF file: using metadata file") - if _, err = observerMinReqs(); err != nil { - return fmt.Errorf("tetragon, aborting minimum requirements not met: %w", err) - } - - var ( - loadedMaps []*program.Map - loadedProgs []*program.Program - ) - - s.createDirs(bpfDir) - defer func() { - if err != nil { - for _, m := range loadedMaps { - m.Unload(true) - } - for _, p := range loadedProgs { - unloadProgram(p, true) - } - s.removeDirs() - } - }() - - l := logger.GetLogger() - - l.WithField("name", s.Name).Info("Loading sensor") - if s.Loaded { - return fmt.Errorf("loading sensor %s failed: sensor already loaded", s.Name) - } - - _, verStr, _ := kernels.GetKernelVersion(option.Config.KernelVersion, option.Config.ProcFS) - l.Infof("Loading kernel version %s", verStr) - - if err = s.FindPrograms(); err != nil { - return fmt.Errorf("tetragon, aborting could not find BPF programs: %w", err) - } - - for _, m := range s.Maps { - if err = s.loadMap(bpfDir, m); err != nil { - return fmt.Errorf("tetragon, aborting could not load sensor BPF maps: %w", err) - } - loadedMaps = append(loadedMaps, m) - } - - for _, p := range s.Progs { - if p.LoadState.IsLoaded() { - l.WithField("prog", p.Name).Info("BPF prog is already loaded, incrementing reference count") - p.LoadState.RefInc() - continue - } - - if err = observerLoadInstance(bpfDir, p, s.Maps); err != nil { - return err - } - p.LoadState.RefInc() - loadedProgs = append(loadedProgs, p) - l.WithField("prog", p.Name).WithField("label", p.Label).Debugf("BPF prog was loaded") - } - - // Add the *loaded* programs and maps, so they can be unloaded later - progsAdd(s.Progs) - AllMaps = append(AllMaps, s.Maps...) - - if s.PostLoadHook != nil { - if err := s.PostLoadHook(); err != nil { - logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Post load hook failed") - } - } - - // cleanup the BTF once we have loaded all sensor's program - btf.FlushKernelSpec() - - l.WithFields(logrus.Fields{ - "sensor": s.Name, - "maps": loadedMaps, - "progs": loadedProgs, - }).Infof("Loaded BPF maps and events for sensor successfully") - s.Loaded = true - return nil -} - func (s *Sensor) setMapPinPath(m *program.Map) { policy := s.policyDir() switch m.Type { @@ -122,6 +31,16 @@ func (s *Sensor) setMapPinPath(m *program.Map) { } } +func (s *Sensor) preLoadMaps(bpfDir string, loadedMaps []*program.Map) ([]*program.Map, error) { + for _, m := range s.Maps { + if err := s.loadMap(bpfDir, m); err != nil { + return loadedMaps, fmt.Errorf("tetragon, aborting could not load sensor BPF maps: %w", err) + } + loadedMaps = append(loadedMaps, m) + } + return loadedMaps, nil +} + // loadMap loads BPF map in the sensor. func (s *Sensor) loadMap(bpfDir string, m *program.Map) error { l := logger.GetLogger() @@ -282,3 +201,11 @@ func observerMinReqs() (bool, error) { } return true, nil } + +func flushKernelSpec() { + btf.FlushKernelSpec() +} + +func getCachedBTFFile() string { + return cachedbtf.GetCachedBTFFile() +} diff --git a/pkg/sensors/load_windows.go b/pkg/sensors/load_windows.go index 863827a28ee..b3e016a623e 100644 --- a/pkg/sensors/load_windows.go +++ b/pkg/sensors/load_windows.go @@ -14,84 +14,6 @@ import ( "github.com/sirupsen/logrus" ) -// Load loads the sensor, by loading all the BPF programs and maps. -func (s *Sensor) Load(bpfDir string) (err error) { - if s == nil { - return nil - } - - if s.Destroyed { - return fmt.Errorf("sensor %s has been previously destroyed, please recreate it before loading", s.Name) - } - if _, err = observerMinReqs(); err != nil { - return fmt.Errorf("tetragon, aborting minimum requirements not met: %w", err) - } - - var ( - loadedMaps []*program.Map - loadedProgs []*program.Program - ) - - s.createDirs(bpfDir) - defer func() { - if err != nil { - for _, m := range loadedMaps { - m.Unload(true) - } - for _, p := range loadedProgs { - unloadProgram(p, true) - } - s.removeDirs() - } - }() - - l := logger.GetLogger() - - l.WithField("name", s.Name).Info("Loading sensor") - if s.Loaded { - return fmt.Errorf("loading sensor %s failed: sensor already loaded", s.Name) - } - - if err = s.FindPrograms(); err != nil { - return fmt.Errorf("tetragon, aborting could not find BPF programs: %w", err) - } - // Comparing with Linux, why are maps not loaded here ? - // In windows, we load collection directly and do not load specs. - // The collection loads maps for us. - for _, p := range s.Progs { - if p.LoadState.IsLoaded() { - l.WithField("prog", p.Name).Info("BPF prog is already loaded, incrementing reference count") - p.LoadState.RefInc() - continue - } - - if err = observerLoadInstance(bpfDir, p, s.Maps); err != nil { - return err - } - p.LoadState.RefInc() - loadedProgs = append(loadedProgs, p) - l.WithField("prog", p.Name).WithField("label", p.Label).Debugf("BPF prog was loaded") - } - - // Add the *loaded* programs and maps, so they can be unloaded later - progsAdd(s.Progs) - AllMaps = append(AllMaps, s.Maps...) - - if s.PostLoadHook != nil { - if err := s.PostLoadHook(); err != nil { - logger.GetLogger().WithError(err).WithField("sensor", s.Name).Warn("Post load hook failed") - } - } - - l.WithFields(logrus.Fields{ - "sensor": s.Name, - "maps": loadedMaps, - "progs": loadedProgs, - }).Infof("Loaded BPF maps and events for sensor successfully") - s.Loaded = true - return nil -} - func observerLoadInstance(bpfDir string, load *program.Program, maps []*program.Map) error { version, _, err := kernels.GetKernelVersion(option.Config.KernelVersion, option.Config.ProcFS) if err != nil { @@ -129,3 +51,15 @@ func loadInstance(bpfDir string, load *program.Program, maps []*program.Map, ver func observerMinReqs() (bool, error) { return true, nil } + +func flushKernelSpec() { + return +} + +func (s *Sensor) preLoadMaps(bpfDir string, loadedMaps []*program.Map) ([]*program.Map, error) { + return nil, nil +} + +func getCachedBTFFile() string { + return "" +} diff --git a/pkg/sensors/program/loader.go b/pkg/sensors/program/loader.go new file mode 100644 index 00000000000..131b0dab6f4 --- /dev/null +++ b/pkg/sensors/program/loader.go @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon +package program + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/cilium/ebpf" + "github.com/cilium/tetragon/pkg/sensors/unloader" +) + +// AttachFunc is the type for the various attachment functions. The function is +// given the program and it's up to it to close it. +type AttachFunc func(*ebpf.Collection, *ebpf.CollectionSpec, *ebpf.Program, *ebpf.ProgramSpec) (unloader.Unloader, error) + +type OpenFunc func(*ebpf.CollectionSpec) error + +type LoadOpts struct { + Attach AttachFunc + Open OpenFunc + Maps []*Map +} + +func linkPinPath(bpfDir string, load *Program, extra ...string) string { + pinPath := filepath.Join(bpfDir, load.PinPath, "link") + if len(extra) != 0 { + pinPath = pinPath + "_" + strings.Join(extra, "_") + } + return pinPath +} + +func RawAttach(targetFD int) AttachFunc { + return RawAttachWithFlags(targetFD, 0) +} + +func NoAttach() AttachFunc { + return func(_ *ebpf.Collection, _ *ebpf.CollectionSpec, + prog *ebpf.Program, _ *ebpf.ProgramSpec) (unloader.Unloader, error) { + return unloader.ChainUnloader{ + unloader.ProgUnloader{ + Prog: prog, + }, + }, nil + } +} + +// MissingConstantsError is returned by [rewriteConstants]. +type MissingConstantsError struct { + // The constants missing from .rodata. + Constants []string +} + +func (m *MissingConstantsError) Error() string { + return fmt.Sprintf("some constants are missing from .rodata: %s", strings.Join(m.Constants, ", ")) +} + +// The loadProgram loads and attach bpf object @load. It is expected that user +// provides @loadOpts with mandatory attach function and optional open function. +// +// The load process is roughly as follows: +// +// - load object | ebpf.LoadCollectionSpec +// - open callback | loadOpts.open(spec) +// - open refferenced maps | +// - creates collection | ebpf.NewCollectionWithOptions(spec, opts) +// - install tail calls | loadOpts.ci +// - load maps with values | +// - pin main program | +// - attach callback | loadOpts.attach(coll, spec, prog, progSpec) +// - print loaded progs/maps | if KeepCollection == true +// +// The @loadOpts.open callback can be used to customize ebpf.CollectionSpec +// before it's loaded into kernel (like disable/enable programs). +// +// The @loadOpts.attach callback is used to actually attach main object program +// to desired function/symbol/whatever.. +// +// The @loadOpts.ci defines specific installation of tailcalls in object. + +func loadProgram( + bpfDir string, + load *Program, + opts *LoadOpts, + verbose int, +) error { + + // Attach function is mandatory + if opts.Attach == nil { + return fmt.Errorf("attach function is not provided") + } + + lc, err := doLoadProgram(bpfDir, load, opts, verbose) + if err != nil { + return err + } + if KeepCollection { + load.LC = filterLoadedCollection(lc) + printLoadedCollection(load.Name, load.LC) + } + return nil +} + +func LoadProgram( + bpfDir string, + load *Program, + maps []*Map, + attach AttachFunc, + verbose int, +) error { + return loadProgram(bpfDir, load, &LoadOpts{Attach: attach, Maps: maps}, verbose) +} + +func LoadProgramOpts( + bpfDir string, + load *Program, + opts *LoadOpts, + verbose int, +) error { + return loadProgram(bpfDir, load, opts, verbose) +} diff --git a/pkg/sensors/program/loader_linux.go b/pkg/sensors/program/loader_linux.go index 4ce70f782a6..71382578f1f 100644 --- a/pkg/sensors/program/loader_linux.go +++ b/pkg/sensors/program/loader_linux.go @@ -19,26 +19,6 @@ import ( "github.com/cilium/tetragon/pkg/sensors/unloader" ) -// AttachFunc is the type for the various attachment functions. The function is -// given the program and it's up to it to close it. -type AttachFunc func(*ebpf.Collection, *ebpf.CollectionSpec, *ebpf.Program, *ebpf.ProgramSpec) (unloader.Unloader, error) - -type OpenFunc func(*ebpf.CollectionSpec) error - -type LoadOpts struct { - Attach AttachFunc - Open OpenFunc - Maps []*Map -} - -func linkPinPath(bpfDir string, load *Program, extra ...string) string { - pinPath := filepath.Join(bpfDir, load.PinPath, "link") - if len(extra) != 0 { - pinPath = pinPath + "_" + strings.Join(extra, "_") - } - return pinPath -} - func linkPin(lnk link.Link, bpfDir string, load *Program, extra ...string) error { // pinned link is not supported if !bpf.HasLinkPin() { @@ -54,10 +34,6 @@ func linkPin(lnk link.Link, bpfDir string, load *Program, extra ...string) error return nil } -func RawAttach(targetFD int) AttachFunc { - return RawAttachWithFlags(targetFD, 0) -} - func RawAttachWithFlags(targetFD int, flags uint32) AttachFunc { return func(_ *ebpf.Collection, _ *ebpf.CollectionSpec, prog *ebpf.Program, spec *ebpf.ProgramSpec) (unloader.Unloader, error) { @@ -379,17 +355,6 @@ func MultiUprobeAttach(load *Program) AttachFunc { } } -func NoAttach() AttachFunc { - return func(_ *ebpf.Collection, _ *ebpf.CollectionSpec, - prog *ebpf.Program, _ *ebpf.ProgramSpec) (unloader.Unloader, error) { - return unloader.ChainUnloader{ - unloader.ProgUnloader{ - Prog: prog, - }, - }, nil - } -} - func TracingAttach(load *Program, bpfDir string) AttachFunc { return func(_ *ebpf.Collection, _ *ebpf.CollectionSpec, prog *ebpf.Program, spec *ebpf.ProgramSpec) (unloader.Unloader, error) { @@ -747,16 +712,6 @@ func installTailCalls(bpfDir string, spec *ebpf.CollectionSpec, coll *ebpf.Colle return nil } -// MissingConstantsError is returned by [rewriteConstants]. -type MissingConstantsError struct { - // The constants missing from .rodata. - Constants []string -} - -func (m *MissingConstantsError) Error() string { - return fmt.Sprintf("some constants are missing from .rodata: %s", strings.Join(m.Constants, ", ")) -} - func rewriteConstants(spec *ebpf.CollectionSpec, consts map[string]interface{}) error { var missing []string @@ -1061,68 +1016,3 @@ func doLoadProgram( } return nil, nil } - -// The loadProgram loads and attach bpf object @load. It is expected that user -// provides @loadOpts with mandatory attach function and optional open function. -// -// The load process is roughly as follows: -// -// - load object | ebpf.LoadCollectionSpec -// - open callback | loadOpts.open(spec) -// - open refferenced maps | -// - creates collection | ebpf.NewCollectionWithOptions(spec, opts) -// - install tail calls | loadOpts.ci -// - load maps with values | -// - pin main program | -// - attach callback | loadOpts.attach(coll, spec, prog, progSpec) -// - print loaded progs/maps | if KeepCollection == true -// -// The @loadOpts.open callback can be used to customize ebpf.CollectionSpec -// before it's loaded into kernel (like disable/enable programs). -// -// The @loadOpts.attach callback is used to actually attach main object program -// to desired function/symbol/whatever.. -// -// The @loadOpts.ci defines specific installation of tailcalls in object. - -func loadProgram( - bpfDir string, - load *Program, - opts *LoadOpts, - verbose int, -) error { - - // Attach function is mandatory - if opts.Attach == nil { - return fmt.Errorf("attach function is not provided") - } - - lc, err := doLoadProgram(bpfDir, load, opts, verbose) - if err != nil { - return err - } - if KeepCollection { - load.LC = filterLoadedCollection(lc) - printLoadedCollection(load.Name, load.LC) - } - return nil -} - -func LoadProgram( - bpfDir string, - load *Program, - maps []*Map, - attach AttachFunc, - verbose int, -) error { - return loadProgram(bpfDir, load, &LoadOpts{Attach: attach, Maps: maps}, verbose) -} - -func LoadProgramOpts( - bpfDir string, - load *Program, - opts *LoadOpts, - verbose int, -) error { - return loadProgram(bpfDir, load, opts, verbose) -} diff --git a/pkg/sensors/program/loader_windows.go b/pkg/sensors/program/loader_windows.go new file mode 100644 index 00000000000..612ec144b80 --- /dev/null +++ b/pkg/sensors/program/loader_windows.go @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package program + +import ( + "errors" + "fmt" + "os" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/cilium/tetragon/pkg/bpf" + "github.com/cilium/tetragon/pkg/constants" + "github.com/cilium/tetragon/pkg/logger" + "github.com/cilium/tetragon/pkg/sensors/unloader" + "golang.org/x/sys/windows" +) + +var ( + notSupportedWinErr = errors.New("not supported on windows") + programTypeProcessGUID = makeGUID(0x22ea7b37, 0x1043, 0x4d0d, [8]byte{0xb6, 0x0d, 0xca, 0xfa, 0x1c, 0x7b, 0x63, 0x8e}) + attachTypeProcessGUID = makeGUID(0x66e20687, 0x9805, 0x4458, [8]byte{0xa0, 0xdb, 0x38, 0xe2, 0x20, 0xd3, 0x16, 0x85}) +) + +func makeGUID(data1 uint32, data2 uint16, data3 uint16, data4 [8]byte) windows.GUID { + return windows.GUID{Data1: data1, Data2: data2, Data3: data3, Data4: data4} +} + +func winAttachStub(_ *ebpf.Collection, _ *ebpf.CollectionSpec, + prog *ebpf.Program, spec *ebpf.ProgramSpec) (unloader.Unloader, error) { + + return nil, notSupportedWinErr +} + +func RawAttachWithFlags(targetFD int, flags uint32) AttachFunc { + return winAttachStub +} + +func windowsAttach(load *Program, prog *ebpf.Program, spec *ebpf.ProgramSpec, + symbol string, bpfDir string, extra ...string) (unloader.Unloader, error) { + + attachType, err := ebpf.WindowsAttachTypeForGUID(attachTypeProcessGUID.String()) + if err != nil { + return nil, err + } + + link, err := link.AttachRawLink(link.RawLinkOptions{ + Program: prog, + Attach: attachType, + }) + if err != nil { + return nil, err + } + return unloader.ChainUnloader{ + unloader.ProgUnloader{ + Prog: prog, + }, + unloader.LinkUnloader{ + Link: link, + }, + }, nil + +} + +func WindowsAttach(load *Program, bpfDir string) AttachFunc { + return func(coll *ebpf.Collection, collSpec *ebpf.CollectionSpec, + prog *ebpf.Program, spec *ebpf.ProgramSpec) (unloader.Unloader, error) { + + return windowsAttach(load, prog, spec, load.Attach, bpfDir) + } +} + +func LoadWindowsProgram(bpfDir string, load *Program, maps []*Map, verbose int) error { + opts := &LoadOpts{ + Attach: WindowsAttach(load, bpfDir), + } + return loadProgram(bpfDir, load, opts, verbose) +} + +func LoadTracepointProgram(bpfDir string, load *Program, maps []*Map, verbose int) error { + return constants.ErrWindowsNotSupported +} + +func LoadKprobeProgramAttachMany(bpfDir string, load *Program, syms []string, maps []*Map, verbose int) error { + return constants.ErrWindowsNotSupported +} + +func LoadMultiKprobeProgram(bpfDir string, load *Program, maps []*Map, verbose int) error { + return constants.ErrWindowsNotSupported +} + +func LoadFmodRetProgram(bpfDir string, load *Program, maps []*Map, progName string, verbose int) error { + return constants.ErrWindowsNotSupported +} + +func doLoadProgram( + bpfDir string, + load *Program, + loadOpts *LoadOpts, + verbose int, +) (*LoadedCollection, error) { + + coll, err := ebpf.LoadCollection(load.Name) + if err != nil { + logger.GetLogger().WithError(err).WithField("Error ", err.Error()).Warn(" Failed to load Native Windows Collection ") + return nil, err + } + bpf.SetExecCollection(coll) + + collMaps := map[ebpf.MapID]*ebpf.Map{} + // we need a mapping by ID + for _, m := range coll.Maps { + + info, err := m.Info() + if err != nil { + logger.GetLogger().WithError(err).WithField("map", m.String()).Warn("failed to retrieve BPF map info") + continue + } + id, available := info.ID() + if !available { + logger.GetLogger().WithField("map", m.String()).Warn("failed to retrieve BPF map ID, you might be running <4.13") + continue + } + collMaps[id] = m + + // In Windows, this is where we pin maps. + // ToDo: Pinned maps do not get unpinned when tetragon stops, + // This is to be uncommented once that issue is fixed. + // We do not need pinned maps for events + // if _, exist := load.PinMap[info.Name]; exist { + // pinPath := load.Attach + "::" + info.Name + // err = m.Pin(pinPath) + // if err != nil { + // logger.GetLogger().WithField("map", m.String()).Warn("failed to pin map") + // } else { + // logger.GetLogger().WithField("map tp path ", pinPath).Info("Successfully pinned") + // } + // } + } + + load.LoadedMapsInfo = map[int]bpf.ExtendedMapInfo{} + + var prog *ebpf.Program + for _, p := range coll.Programs { + + i, err := p.Info() + if i.Name == load.Label { + prog = p + } + if err != nil { + logger.GetLogger().WithError(err).WithField("program", p.String()).Warn("failed to retrieve BPF program info, you might be running <4.10") + break + } + ids, available := i.MapIDs() + if !available { + logger.GetLogger().WithField("program", p.String()).Warn("failed to retrieve BPF program map IDs, you might be running <4.15") + break + } + for _, id := range ids { + if _, exist := load.LoadedMapsInfo[int(id)]; exist { + continue + } + xInfo, err := bpf.ExtendedInfoFromMap(collMaps[id]) + if err != nil { + logger.GetLogger().WithError(err).WithField("mapID", id).Warn("failed to retrieve extended map info") + break + } + load.LoadedMapsInfo[int(id)] = xInfo + } + } + + 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, pinPath, mapLoad.Index); err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("populating map failed as map '%s' was not found from collection", mapLoad.Name) + } + } + if prog == nil { + return nil, fmt.Errorf("program for section '%s' not found", load.Label) + } + + pinPath := load.PinPath + if _, err := os.Stat(pinPath); err == nil { + logger.GetLogger().Debugf("Pin file '%s' already exists, repinning", load.PinPath) + if err := os.Remove(pinPath); err != nil { + logger.GetLogger().Warnf("Unpinning '%s' failed: %s", pinPath, err) + } + } + + // Clone the program so it can be passed on to attach function and unloader after + // we close the collection. + prog, err = prog.Clone() + if err != nil { + return nil, fmt.Errorf("failed to clone program '%s': %w", load.Label, err) + } + + if err := prog.Pin(pinPath); err != nil { + return nil, fmt.Errorf("pinning '%s' to '%s' failed: %w", load.Label, pinPath, err) + } + + load.unloader, err = loadOpts.Attach(coll, nil, prog, nil) + if err != nil { + if err := prog.Unpin(); err != nil { + logger.GetLogger().Warnf("Unpinning '%s' failed: %w", pinPath, err) + } + return nil, err + } + + load.Prog = prog + + // in KernelTypes, we use a non-standard BTF which is possibly annotated with symbols + // from kernel modules. At this point we don't need that anymore, so we can release + // the memory from it. + load.KernelTypes = nil + + // Copy the loaded collection before it's destroyed + if KeepCollection { + return copyLoadedCollection(coll) + } + return nil, nil +}