github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/mount/tracer/tracer.go (about)

     1  // Copyright 2019-2023 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  //go:build !withoutebpf
    16  
    17  package tracer
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"unsafe"
    24  
    25  	"github.com/cilium/ebpf"
    26  	"github.com/cilium/ebpf/link"
    27  	"github.com/cilium/ebpf/perf"
    28  
    29  	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
    30  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
    31  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/mount/types"
    32  	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    33  )
    34  
    35  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -no-global-types -target bpfel -cc clang -cflags ${CFLAGS} -type event -type op mountsnoop ./bpf/mountsnoop.bpf.c -- -I./bpf/
    36  
    37  type Config struct {
    38  	MountnsMap *ebpf.Map
    39  }
    40  
    41  type Tracer struct {
    42  	config        *Config
    43  	enricher      gadgets.DataEnricherByMntNs
    44  	eventCallback func(*types.Event)
    45  
    46  	objs            mountsnoopObjects
    47  	mountEnterLink  link.Link
    48  	umountEnterLink link.Link
    49  	mountExitLink   link.Link
    50  	umountExitLink  link.Link
    51  	reader          *perf.Reader
    52  }
    53  
    54  func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
    55  	eventCallback func(*types.Event),
    56  ) (*Tracer, error) {
    57  	t := &Tracer{
    58  		config:        config,
    59  		enricher:      enricher,
    60  		eventCallback: eventCallback,
    61  	}
    62  
    63  	if err := t.install(); err != nil {
    64  		t.close()
    65  		return nil, err
    66  	}
    67  
    68  	go t.run()
    69  
    70  	return t, nil
    71  }
    72  
    73  // Stop stops the tracer
    74  // TODO: Remove after refactoring
    75  func (t *Tracer) Stop() {
    76  	t.close()
    77  }
    78  
    79  func (t *Tracer) close() {
    80  	t.mountEnterLink = gadgets.CloseLink(t.mountEnterLink)
    81  	t.umountEnterLink = gadgets.CloseLink(t.umountEnterLink)
    82  	t.mountExitLink = gadgets.CloseLink(t.mountExitLink)
    83  	t.umountExitLink = gadgets.CloseLink(t.umountExitLink)
    84  
    85  	if t.reader != nil {
    86  		t.reader.Close()
    87  	}
    88  
    89  	t.objs.Close()
    90  }
    91  
    92  func (t *Tracer) install() error {
    93  	var err error
    94  	spec, err := loadMountsnoop()
    95  	if err != nil {
    96  		return fmt.Errorf("loading ebpf program: %w", err)
    97  	}
    98  
    99  	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, nil, &t.objs); err != nil {
   100  		return fmt.Errorf("loading ebpf spec: %w", err)
   101  	}
   102  
   103  	t.mountEnterLink, err = link.Tracepoint("syscalls", "sys_enter_mount", t.objs.IgMountE, nil)
   104  	if err != nil {
   105  		return fmt.Errorf("attaching tracepoint: %w", err)
   106  	}
   107  
   108  	t.mountExitLink, err = link.Tracepoint("syscalls", "sys_exit_mount", t.objs.IgMountX, nil)
   109  	if err != nil {
   110  		return fmt.Errorf("attaching tracepoint: %w", err)
   111  	}
   112  
   113  	t.umountEnterLink, err = link.Tracepoint("syscalls", "sys_enter_umount", t.objs.IgUmountE, nil)
   114  	if err != nil {
   115  		return fmt.Errorf("attaching tracepoint: %w", err)
   116  	}
   117  
   118  	t.umountExitLink, err = link.Tracepoint("syscalls", "sys_exit_umount", t.objs.IgUmountX, nil)
   119  	if err != nil {
   120  		return fmt.Errorf("attaching tracepoint: %w", err)
   121  	}
   122  
   123  	t.reader, err = perf.NewReader(t.objs.mountsnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
   124  	if err != nil {
   125  		return fmt.Errorf("creating perf ring buffer: %w", err)
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  func (t *Tracer) run() {
   132  	for {
   133  		record, err := t.reader.Read()
   134  		if err != nil {
   135  			if errors.Is(err, perf.ErrClosed) {
   136  				// nothing to do, we're done
   137  				return
   138  			}
   139  
   140  			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
   141  			t.eventCallback(types.Base(eventtypes.Err(msg)))
   142  			return
   143  		}
   144  
   145  		if record.LostSamples > 0 {
   146  			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
   147  			t.eventCallback(types.Base(eventtypes.Warn(msg)))
   148  			continue
   149  		}
   150  
   151  		bpfEvent := (*mountsnoopEvent)(unsafe.Pointer(&record.RawSample[0]))
   152  
   153  		event := types.Event{
   154  			Event: eventtypes.Event{
   155  				Type:      eventtypes.NORMAL,
   156  				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
   157  			},
   158  			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MountNsId},
   159  			Pid:           bpfEvent.Pid,
   160  			Tid:           bpfEvent.Tid,
   161  			Comm:          gadgets.FromCString(bpfEvent.Comm[:]),
   162  			Retval:        int(bpfEvent.Ret),
   163  			Latency:       bpfEvent.Delta,
   164  			Fs:            gadgets.FromCString(bpfEvent.Fs[:]),
   165  			Source:        gadgets.FromCString(bpfEvent.Src[:]),
   166  			Target:        gadgets.FromCString(bpfEvent.Dest[:]),
   167  			Data:          gadgets.FromCString(bpfEvent.Data[:]),
   168  		}
   169  
   170  		switch bpfEvent.Op {
   171  		case mountsnoopOpMOUNT:
   172  			event.Operation = "mount"
   173  		case mountsnoopOpUMOUNT:
   174  			event.Operation = "umount"
   175  		default:
   176  			event.Operation = "unknown"
   177  		}
   178  
   179  		event.Flags = DecodeFlags(bpfEvent.Flags)
   180  
   181  		if t.enricher != nil {
   182  			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
   183  		}
   184  
   185  		t.eventCallback(&event)
   186  	}
   187  }
   188  
   189  // --- Registry changes
   190  
   191  func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
   192  	defer t.close()
   193  	if err := t.install(); err != nil {
   194  		return fmt.Errorf("installing tracer: %w", err)
   195  	}
   196  
   197  	go t.run()
   198  	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
   199  
   200  	return nil
   201  }
   202  
   203  func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
   204  	t.config.MountnsMap = mountnsMap
   205  }
   206  
   207  func (t *Tracer) SetEventHandler(handler any) {
   208  	nh, ok := handler.(func(ev *types.Event))
   209  	if !ok {
   210  		panic("event handler invalid")
   211  	}
   212  	t.eventCallback = nh
   213  }
   214  
   215  func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
   216  	tracer := &Tracer{
   217  		config: &Config{},
   218  	}
   219  	return tracer, nil
   220  }