github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/ebpf/ebpf.go (about)

     1  // Copyright 2024 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package ebpfoperator provides an operator that is capable of analyzing and running
    16  // an eBFP based gadget.
    17  package ebpfoperator
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"reflect"
    26  	"strings"
    27  	"sync"
    28  
    29  	"github.com/cilium/ebpf"
    30  	"github.com/cilium/ebpf/btf"
    31  	"github.com/cilium/ebpf/link"
    32  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    33  	"github.com/spf13/viper"
    34  
    35  	containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
    36  	"github.com/inspektor-gadget/inspektor-gadget/pkg/datasource"
    37  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api"
    38  	apihelpers "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api-helpers"
    39  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
    40  	"github.com/inspektor-gadget/inspektor-gadget/pkg/logger"
    41  	"github.com/inspektor-gadget/inspektor-gadget/pkg/networktracer"
    42  	"github.com/inspektor-gadget/inspektor-gadget/pkg/oci"
    43  	"github.com/inspektor-gadget/inspektor-gadget/pkg/operators"
    44  	"github.com/inspektor-gadget/inspektor-gadget/pkg/params"
    45  	"github.com/inspektor-gadget/inspektor-gadget/pkg/socketenricher"
    46  	"github.com/inspektor-gadget/inspektor-gadget/pkg/tchandler"
    47  	"github.com/inspektor-gadget/inspektor-gadget/pkg/uprobetracer"
    48  )
    49  
    50  const (
    51  	eBPFObjectMediaType = "application/vnd.gadget.ebpf.program.v1+binary"
    52  
    53  	typeSplitter = "___"
    54  
    55  	ParamIface       = "iface"
    56  	ParamTraceKernel = "trace-pipe"
    57  
    58  	kernelTypesVar = "kernelTypes"
    59  )
    60  
    61  type param struct {
    62  	*api.Param
    63  	fromEbpf bool
    64  }
    65  
    66  // ebpfOperator reads ebpf programs from OCI images and runs them
    67  type ebpfOperator struct{}
    68  
    69  func (o *ebpfOperator) Name() string {
    70  	return "ebpf"
    71  }
    72  
    73  func (o *ebpfOperator) Description() string {
    74  	return "handles ebpf programs"
    75  }
    76  
    77  func (o *ebpfOperator) InstantiateImageOperator(
    78  	gadgetCtx operators.GadgetContext,
    79  	desc ocispec.Descriptor,
    80  	paramValues api.ParamValues,
    81  ) (
    82  	operators.ImageOperatorInstance, error,
    83  ) {
    84  	r, err := oci.GetContentFromDescriptor(gadgetCtx.Context(), desc)
    85  	if err != nil {
    86  		return nil, fmt.Errorf("getting ebpf binary: %w", err)
    87  	}
    88  	program, err := io.ReadAll(r)
    89  	if err != nil {
    90  		r.Close()
    91  		return nil, fmt.Errorf("reading ebpf binary: %w", err)
    92  	}
    93  	r.Close()
    94  
    95  	// TODO: do some pre-checks in here, maybe validate hashes, signatures, etc.
    96  
    97  	newInstance := &ebpfInstance{
    98  		gadgetCtx: gadgetCtx, // context usually should not be stored, but should we really carry it through all funcs?
    99  
   100  		logger:  gadgetCtx.Logger(),
   101  		program: program,
   102  
   103  		// Preallocate maps
   104  		tracers:      make(map[string]*Tracer),
   105  		structs:      make(map[string]*Struct),
   106  		snapshotters: make(map[string]*Snapshotter),
   107  		params:       make(map[string]*param),
   108  
   109  		containers: make(map[string]*containercollection.Container),
   110  
   111  		enums:      make(map[string]*btf.Enum),
   112  		converters: make(map[datasource.DataSource][]func(ds datasource.DataSource, data datasource.Data) error),
   113  
   114  		vars: make(map[string]*ebpfVar),
   115  
   116  		networkTracers: make(map[string]*networktracer.Tracer[api.GadgetData]),
   117  		tcHandlers:     make(map[string]*tchandler.Handler),
   118  		uprobeTracers:  make(map[string]*uprobetracer.Tracer[api.GadgetData]),
   119  
   120  		paramValues: paramValues,
   121  	}
   122  
   123  	cfg, ok := gadgetCtx.GetVar("config")
   124  	if !ok {
   125  		return nil, fmt.Errorf("missing configuration")
   126  	}
   127  	v, ok := cfg.(*viper.Viper)
   128  	if !ok {
   129  		return nil, fmt.Errorf("invalid configuration format")
   130  	}
   131  	newInstance.config = v
   132  
   133  	err = newInstance.init(gadgetCtx)
   134  	if err != nil {
   135  		return nil, fmt.Errorf("initializing ebpf gadget: %w", err)
   136  	}
   137  
   138  	return newInstance, nil
   139  }
   140  
   141  type ebpfInstance struct {
   142  	mu sync.Mutex
   143  
   144  	config *viper.Viper
   145  
   146  	program        []byte
   147  	logger         logger.Logger
   148  	collectionSpec *ebpf.CollectionSpec
   149  	collection     *ebpf.Collection
   150  
   151  	tracers      map[string]*Tracer
   152  	structs      map[string]*Struct
   153  	snapshotters map[string]*Snapshotter
   154  	params       map[string]*param
   155  	paramValues  map[string]string
   156  
   157  	networkTracers map[string]*networktracer.Tracer[api.GadgetData]
   158  	tcHandlers     map[string]*tchandler.Handler
   159  	uprobeTracers  map[string]*uprobetracer.Tracer[api.GadgetData]
   160  
   161  	// map from ebpf variable name to ebpfVar struct
   162  	vars map[string]*ebpfVar
   163  
   164  	links []link.Link
   165  
   166  	containers map[string]*containercollection.Container
   167  
   168  	enums      map[string]*btf.Enum
   169  	converters map[datasource.DataSource][]func(ds datasource.DataSource, data datasource.Data) error
   170  
   171  	gadgetCtx operators.GadgetContext
   172  }
   173  
   174  func (i *ebpfInstance) loadSpec() error {
   175  	progReader := bytes.NewReader(i.program)
   176  	spec, err := ebpf.LoadCollectionSpecFromReader(progReader)
   177  	if err != nil {
   178  		return fmt.Errorf("loading spec: %w", err)
   179  	}
   180  	i.collectionSpec = spec
   181  	return nil
   182  }
   183  
   184  func (i *ebpfInstance) analyze() error {
   185  	prefixLookups := []populateEntry{
   186  		{
   187  			prefixFunc:   hasPrefix(tracerInfoPrefix),
   188  			validator:    i.validateGlobalConstVoidPtrVar,
   189  			populateFunc: i.populateTracer,
   190  		},
   191  		{
   192  			prefixFunc:   hasPrefix(snapshottersPrefix),
   193  			validator:    i.validateGlobalConstVoidPtrVar,
   194  			populateFunc: i.populateSnapshotter,
   195  		},
   196  		{
   197  			prefixFunc:   hasPrefix(paramPrefix),
   198  			validator:    i.validateGlobalConstVoidPtrVar,
   199  			populateFunc: i.populateParam,
   200  		},
   201  		// {
   202  		// 	prefixFunc:   hasPrefix(tracerMapPrefix),
   203  		// 	validator:    i.validateGlobalConstVoidPtrVar,
   204  		// 	populateFunc: i.populateMap,
   205  		// },
   206  		{
   207  			prefixFunc: func(s string) (string, bool) {
   208  				// Exceptions for backwards-compatibility
   209  				if s == gadgets.MntNsFilterMapName {
   210  					return gadgets.MntNsFilterMapName, true
   211  				}
   212  				if s == socketenricher.SocketsMapName {
   213  					return socketenricher.SocketsMapName, true
   214  				}
   215  				return "", false
   216  			},
   217  			populateFunc: i.populateMap,
   218  		},
   219  		{
   220  			prefixFunc: func(s string) (string, bool) {
   221  				// Exceptions for backwards-compatibility
   222  				if s == gadgets.FilterByMntNsName {
   223  					return gadgets.FilterByMntNsName, true
   224  				}
   225  				return hasPrefix(varPrefix)(s)
   226  			},
   227  			validator:    nil,
   228  			populateFunc: i.populateVar,
   229  		},
   230  	}
   231  
   232  	// Iterate over types and populate the gadget
   233  	it := i.collectionSpec.Types.Iterate()
   234  	for it.Next() {
   235  		for _, entry := range prefixLookups {
   236  			typeName, ok := entry.prefixFunc(it.Type.TypeName())
   237  			if !ok {
   238  				continue
   239  			}
   240  			if entry.validator != nil {
   241  				err := entry.validator(it.Type, strings.TrimPrefix(it.Type.TypeName(), typeName))
   242  				if err != nil {
   243  					i.logger.Debugf("type %q error: %v", it.Type.TypeName(), err)
   244  					continue
   245  				}
   246  			}
   247  			err := entry.populateFunc(it.Type, typeName)
   248  			if err != nil {
   249  				return fmt.Errorf("handling type by prefix %q: %w", typeName, err)
   250  			}
   251  		}
   252  	}
   253  
   254  	// Fill param defaults
   255  	err := i.fillParamDefaults()
   256  	if err != nil {
   257  		i.logger.Debugf("error extracting default values for params: %v", err)
   258  	}
   259  
   260  	// Iterate over programs
   261  	for name, program := range i.collectionSpec.Programs {
   262  		i.logger.Debugf("program %q", name)
   263  		i.logger.Debugf("> type       : %s", program.Type.String())
   264  		i.logger.Debugf("> attachType : %s", program.AttachType.String())
   265  		i.logger.Debugf("> sectionName: %s", program.SectionName)
   266  		i.logger.Debugf("> license    : %s", program.License)
   267  	}
   268  	return nil
   269  }
   270  
   271  func (i *ebpfInstance) init(gadgetCtx operators.GadgetContext) error {
   272  	// hack for backward-compability and until we have nicer interfaces available
   273  	gadgetCtx.SetVar("ebpfInstance", i)
   274  
   275  	// loadSpec and analyze could be lazily executed, if the gadget has been cached before
   276  	err := i.loadSpec()
   277  	if err != nil {
   278  		return fmt.Errorf("initializing: %w", err)
   279  	}
   280  	err = i.analyze()
   281  	if err != nil {
   282  		return fmt.Errorf("analyzing: %w", err)
   283  	}
   284  
   285  	err = i.register(gadgetCtx)
   286  	if err != nil {
   287  		return fmt.Errorf("registering datasources: %w", err)
   288  	}
   289  
   290  	err = i.initConverters(gadgetCtx)
   291  	if err != nil {
   292  		return fmt.Errorf("initializing formatters: %w", err)
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  func (i *ebpfInstance) addDataSource(
   299  	gadgetCtx operators.GadgetContext,
   300  	dsType datasource.Type,
   301  	name string,
   302  	size uint32,
   303  	fields []*Field,
   304  ) (
   305  	datasource.DataSource, datasource.FieldAccessor, error,
   306  ) {
   307  	ds, err := gadgetCtx.RegisterDataSource(dsType, name)
   308  	if err != nil {
   309  		return nil, nil, fmt.Errorf("adding tracer datasource: %w", err)
   310  	}
   311  	staticFields := make([]datasource.StaticField, 0, len(fields))
   312  	for _, field := range fields {
   313  		staticFields = append(staticFields, field)
   314  	}
   315  	accessor, err := ds.AddStaticFields(size, staticFields)
   316  	if err != nil {
   317  		return nil, nil, fmt.Errorf("adding fields for datasource: %w", err)
   318  	}
   319  	return ds, accessor, nil
   320  }
   321  
   322  func (i *ebpfInstance) register(gadgetCtx operators.GadgetContext) error {
   323  	// register datasources
   324  	for name, m := range i.tracers {
   325  		ds, accessor, err := i.addDataSource(gadgetCtx, datasource.TypeEvent, name, i.structs[m.StructName].Size, i.structs[m.StructName].Fields)
   326  		if err != nil {
   327  			return fmt.Errorf("adding datasource: %w", err)
   328  		}
   329  		m.accessor = accessor
   330  		m.ds = ds
   331  	}
   332  	for name, m := range i.snapshotters {
   333  		ds, accessor, err := i.addDataSource(gadgetCtx, datasource.TypeEvent, name, i.structs[m.StructName].Size, i.structs[m.StructName].Fields)
   334  		if err != nil {
   335  			return fmt.Errorf("adding datasource: %w", err)
   336  		}
   337  
   338  		// TODO: need a link to find out if this is a snapshotter for network; if so, we can add the netns id
   339  		m.netns, err = ds.AddField("netns",
   340  			datasource.WithTags("type:gadget_netns_id", "name:netns"),
   341  			datasource.WithKind(api.Kind_Uint64))
   342  		if err != nil {
   343  			return fmt.Errorf("adding netnsid")
   344  		}
   345  
   346  		m.accessor = accessor
   347  		m.ds = ds
   348  	}
   349  	return nil
   350  }
   351  
   352  func (i *ebpfInstance) Name() string {
   353  	return "ebpf"
   354  }
   355  
   356  func (i *ebpfInstance) ExtraParams(gadgetCtx operators.GadgetContext) api.Params {
   357  	res := make(api.Params, 0, len(i.params))
   358  	for _, p := range i.params {
   359  		res = append(res, p.Param)
   360  	}
   361  	return res
   362  }
   363  
   364  func (i *ebpfInstance) Prepare(gadgetCtx operators.GadgetContext) error {
   365  	for ds, converters := range i.converters {
   366  		for _, converter := range converters {
   367  			converter := converter
   368  			ds.Subscribe(func(ds datasource.DataSource, data datasource.Data) error {
   369  				return converter(ds, data)
   370  			}, 0)
   371  		}
   372  	}
   373  
   374  	// Create network tracers, one for each socket filter program
   375  	// The same applies to uprobe / uretprobe as well.
   376  	for _, p := range i.collectionSpec.Programs {
   377  		switch p.Type {
   378  		case ebpf.Kprobe:
   379  			if strings.HasPrefix(p.SectionName, "uprobe/") ||
   380  				strings.HasPrefix(p.SectionName, "uretprobe/") ||
   381  				strings.HasPrefix(p.SectionName, "usdt/") {
   382  				uprobeTracer, err := uprobetracer.NewTracer[api.GadgetData](gadgetCtx.Logger())
   383  				if err != nil {
   384  					i.Close()
   385  					return fmt.Errorf("creating uprobe tracer: %w", err)
   386  				}
   387  				i.uprobeTracers[p.Name] = uprobeTracer
   388  			}
   389  		case ebpf.SocketFilter:
   390  			if strings.HasPrefix(p.SectionName, "socket") {
   391  				networkTracer, err := networktracer.NewTracer[api.GadgetData]()
   392  				if err != nil {
   393  					i.Close()
   394  					return fmt.Errorf("creating network tracer: %w", err)
   395  				}
   396  				i.networkTracers[p.Name] = networkTracer
   397  			}
   398  		case ebpf.SchedCLS:
   399  			parts := strings.Split(p.SectionName, "/")
   400  			if len(parts) != 3 {
   401  				return fmt.Errorf("invalid section name %q", p.SectionName)
   402  			}
   403  			if parts[0] != "classifier" {
   404  				return fmt.Errorf("invalid section name %q", p.SectionName)
   405  			}
   406  
   407  			var direction tchandler.AttachmentDirection
   408  
   409  			switch parts[1] {
   410  			case "ingress":
   411  				direction = tchandler.AttachmentDirectionIngress
   412  			case "egress":
   413  				direction = tchandler.AttachmentDirectionEgress
   414  			default:
   415  				return fmt.Errorf("unsupported hook type %q", parts[1])
   416  			}
   417  
   418  			handler, err := tchandler.NewHandler(direction)
   419  			if err != nil {
   420  				i.Close()
   421  				return fmt.Errorf("creating tc network tracer: %w", err)
   422  			}
   423  
   424  			i.tcHandlers[p.Name] = handler
   425  		}
   426  	}
   427  
   428  	if len(i.tcHandlers) > 0 {
   429  		// For now, override enrichment
   430  		gadgetCtx.SetVar("NeedContainerEvents", true)
   431  		i.params["iface"] = &param{
   432  			Param: &api.Param{
   433  				Key:         ParamIface,
   434  				Description: "Network interface to attach to",
   435  			},
   436  		}
   437  	}
   438  
   439  	i.params[ParamTraceKernel] = &param{
   440  		Param: &api.Param{
   441  			Key:          ParamTraceKernel,
   442  			DefaultValue: "false",
   443  			TypeHint:     api.TypeBool,
   444  		},
   445  	}
   446  	return nil
   447  }
   448  
   449  func (i *ebpfInstance) tracePipe(gadgetCtx operators.GadgetContext) error {
   450  	tracePipe, err := os.Open("/sys/kernel/debug/tracing/trace_pipe")
   451  	if err != nil {
   452  		return fmt.Errorf("opening trace_pipe: %w", err)
   453  	}
   454  	go func() {
   455  		<-gadgetCtx.Context().Done()
   456  		tracePipe.Close()
   457  	}()
   458  	go func() {
   459  		log := gadgetCtx.Logger()
   460  
   461  		defer tracePipe.Close()
   462  		scanner := bufio.NewScanner(tracePipe)
   463  		for scanner.Scan() {
   464  			log.Debug(scanner.Text())
   465  		}
   466  	}()
   467  	return nil
   468  }
   469  
   470  func (i *ebpfInstance) Start(gadgetCtx operators.GadgetContext) error {
   471  	i.logger.Debugf("starting ebpfInstance")
   472  
   473  	gadgets.FixBpfKtimeGetBootNs(i.collectionSpec.Programs)
   474  
   475  	parameters := params.Params{}              // used to CopyFromMap
   476  	paramMap := make(map[string]*params.Param) // used for second iteration
   477  	for name, p := range i.params {
   478  		param := apihelpers.ParamToParamDesc(p.Param).ToParam()
   479  		paramMap[name] = param
   480  		parameters = append(parameters, param)
   481  	}
   482  	err := parameters.CopyFromMap(i.paramValues, "")
   483  	if err != nil {
   484  		return fmt.Errorf("parsing parameter values: %w", err)
   485  	}
   486  
   487  	if paramMap[ParamTraceKernel].AsBool() {
   488  		err := i.tracePipe(gadgetCtx)
   489  		if err != nil {
   490  			return err
   491  		}
   492  	}
   493  
   494  	mapReplacements := make(map[string]*ebpf.Map)
   495  	constReplacements := make(map[string]any)
   496  
   497  	// Set gadget params
   498  	for name, p := range i.params {
   499  		if !p.fromEbpf {
   500  			continue
   501  		}
   502  		constReplacements[name] = paramMap[name].AsAny()
   503  		i.logger.Debugf("setting param value %q = %v", name, paramMap[name].AsAny())
   504  	}
   505  
   506  	for _, v := range i.vars {
   507  		res, ok := gadgetCtx.GetVar(v.name)
   508  		if !ok {
   509  			continue
   510  		}
   511  		i.logger.Debugf("got var %q: %+v", v.name, res)
   512  		switch t := res.(type) {
   513  		case *ebpf.Map:
   514  			if t == nil {
   515  				continue
   516  			}
   517  			i.logger.Debugf("replacing map %q", v.name)
   518  			mapReplacements[v.name] = t
   519  		default:
   520  			if !reflect.TypeOf(res).AssignableTo(v.refType) {
   521  				i.logger.Debugf("variable %q can not be set to type %T (expected %s)", v.name, res, v.refType.Name())
   522  				continue
   523  			}
   524  			i.logger.Debugf("setting var %q to %v", v.name, t)
   525  			constReplacements[v.name] = res
   526  		}
   527  	}
   528  
   529  	if err := i.collectionSpec.RewriteConstants(constReplacements); err != nil {
   530  		return fmt.Errorf("rewriting constants: %w", err)
   531  	}
   532  
   533  	i.logger.Debugf("creating ebpf collection")
   534  	opts := ebpf.CollectionOptions{
   535  		MapReplacements: mapReplacements,
   536  	}
   537  
   538  	// check if the btfgen operator has stored the kernel types in the context
   539  	if btfSpecI, ok := gadgetCtx.GetVar(kernelTypesVar); ok {
   540  		gadgetCtx.Logger().Debugf("using kernel types from BTFHub")
   541  		btfSpec, ok := btfSpecI.(*btf.Spec)
   542  		if !ok {
   543  			return fmt.Errorf("invalid BTF spec: expected btf.Spec, got %T", btfSpecI)
   544  		}
   545  		opts.Programs.KernelTypes = btfSpec
   546  	}
   547  	collection, err := ebpf.NewCollectionWithOptions(i.collectionSpec, opts)
   548  	if err != nil {
   549  		return fmt.Errorf("creating eBPF collection: %w", err)
   550  	}
   551  	i.collection = collection
   552  
   553  	for _, tracer := range i.tracers {
   554  		i.logger.Debugf("starting tracer %q", tracer.MapName)
   555  		go func(tracer *Tracer) {
   556  			err := i.runTracer(gadgetCtx, tracer)
   557  			if err != nil {
   558  				i.logger.Errorf("starting tracer: %w", err)
   559  			}
   560  		}(tracer)
   561  	}
   562  
   563  	// Attach programs
   564  	for progName, p := range i.collectionSpec.Programs {
   565  		l, err := i.attachProgram(gadgetCtx, p, i.collection.Programs[progName])
   566  		if err != nil {
   567  			i.Close()
   568  			return fmt.Errorf("attaching eBPF program %q: %w", progName, err)
   569  		}
   570  
   571  		if l != nil {
   572  			i.links = append(i.links, l)
   573  		}
   574  
   575  		// We need to store iterators' links because we need them to run the programs
   576  		if p.Type == ebpf.Tracing && strings.HasPrefix(p.SectionName, iterPrefix) {
   577  			lIter, ok := l.(*link.Iter)
   578  			if !ok {
   579  				i.Close()
   580  				return fmt.Errorf("link is not an iterator")
   581  			}
   582  
   583  			found := false
   584  			for _, snapshotter := range i.snapshotters {
   585  				if _, ok := snapshotter.iterators[progName]; ok {
   586  					snapshotter.links[progName] = &linkSnapshotter{
   587  						link: lIter,
   588  						typ:  p.AttachTo,
   589  					}
   590  					found = true
   591  					break
   592  				}
   593  			}
   594  			if !found {
   595  				i.logger.Warnf("None snapshotter will run iterator %q", progName)
   596  			}
   597  		}
   598  	}
   599  
   600  	err = i.runSnapshotters()
   601  	if err != nil {
   602  		i.Close()
   603  		return fmt.Errorf("running snapshotters: %w", err)
   604  	}
   605  
   606  	return nil
   607  }
   608  
   609  func (i *ebpfInstance) Stop(gadgetCtx operators.GadgetContext) error {
   610  	i.Close()
   611  	return nil
   612  }
   613  
   614  func (i *ebpfInstance) Close() {
   615  	if i.collection != nil {
   616  		i.collection.Close()
   617  		i.collection = nil
   618  	}
   619  	for _, l := range i.links {
   620  		gadgets.CloseLink(l)
   621  	}
   622  	i.links = nil
   623  
   624  	for _, networkTracer := range i.networkTracers {
   625  		networkTracer.Close()
   626  	}
   627  	for _, handler := range i.tcHandlers {
   628  		handler.Close()
   629  	}
   630  	for _, uprobeTracer := range i.uprobeTracers {
   631  		uprobeTracer.Close()
   632  	}
   633  }
   634  
   635  // Using Attacher interface for network tracers for now
   636  
   637  func (i *ebpfInstance) AttachContainer(container *containercollection.Container) error {
   638  	i.mu.Lock()
   639  	i.containers[container.Runtime.ContainerID] = container
   640  	i.mu.Unlock()
   641  
   642  	for _, networkTracer := range i.networkTracers {
   643  		if err := networkTracer.Attach(container.Pid); err != nil {
   644  			return err
   645  		}
   646  	}
   647  
   648  	if ifaceName := i.paramValues[ParamIface]; ifaceName == "" {
   649  		for _, handler := range i.tcHandlers {
   650  			if err := handler.AttachContainer(container); err != nil {
   651  				return err
   652  			}
   653  		}
   654  	}
   655  
   656  	for _, handler := range i.uprobeTracers {
   657  		if err := handler.AttachContainer(container); err != nil {
   658  			return err
   659  		}
   660  	}
   661  
   662  	return nil
   663  }
   664  
   665  func (i *ebpfInstance) DetachContainer(container *containercollection.Container) error {
   666  	i.mu.Lock()
   667  	delete(i.containers, container.Runtime.ContainerID)
   668  	i.mu.Unlock()
   669  
   670  	for _, networkTracer := range i.networkTracers {
   671  		if err := networkTracer.Detach(container.Pid); err != nil {
   672  			return err
   673  		}
   674  	}
   675  
   676  	if ifaceName := i.paramValues[ParamIface]; ifaceName == "" {
   677  		for _, handler := range i.tcHandlers {
   678  			if err := handler.DetachContainer(container); err != nil {
   679  				return err
   680  			}
   681  		}
   682  	}
   683  
   684  	for _, uTracer := range i.uprobeTracers {
   685  		if err := uTracer.DetachContainer(container); err != nil {
   686  			return err
   687  		}
   688  	}
   689  
   690  	return nil
   691  }
   692  
   693  func init() {
   694  	operators.RegisterOperatorForMediaType(eBPFObjectMediaType, &ebpfOperator{})
   695  }