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

     1  // Copyright 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  	"strings"
    24  	"unsafe"
    25  
    26  	"github.com/cilium/ebpf"
    27  	"github.com/cilium/ebpf/btf"
    28  	"github.com/cilium/ebpf/link"
    29  	"github.com/cilium/ebpf/perf"
    30  
    31  	"github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen"
    32  	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
    33  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
    34  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/tcpdrop/types"
    35  	"github.com/inspektor-gadget/inspektor-gadget/pkg/socketenricher"
    36  	"github.com/inspektor-gadget/inspektor-gadget/pkg/tcpbits"
    37  	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    38  )
    39  
    40  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types -type event tcpdrop ./bpf/tcpdrop.bpf.c -- -I./bpf/
    41  type Tracer struct {
    42  	socketEnricherMap *ebpf.Map
    43  	dropReasons       map[int]string
    44  
    45  	eventCallback func(*types.Event)
    46  
    47  	objs         tcpdropObjects
    48  	kfreeSkbLink link.Link
    49  	reader       *perf.Reader
    50  }
    51  
    52  func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
    53  	return &Tracer{}, nil
    54  }
    55  
    56  func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
    57  	defer t.close()
    58  	if err := t.install(); err != nil {
    59  		return fmt.Errorf("installing tracer: %w", err)
    60  	}
    61  
    62  	go t.run()
    63  	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
    64  
    65  	return nil
    66  }
    67  
    68  func (t *Tracer) SetEventHandler(handler any) {
    69  	nh, ok := handler.(func(ev *types.Event))
    70  	if !ok {
    71  		panic("event handler invalid")
    72  	}
    73  	t.eventCallback = nh
    74  }
    75  
    76  func (t *Tracer) SetSocketEnricherMap(m *ebpf.Map) {
    77  	t.socketEnricherMap = m
    78  }
    79  
    80  func (t *Tracer) close() {
    81  	t.kfreeSkbLink = gadgets.CloseLink(t.kfreeSkbLink)
    82  
    83  	if t.reader != nil {
    84  		t.reader.Close()
    85  	}
    86  
    87  	t.objs.Close()
    88  }
    89  
    90  func (t *Tracer) loadDropReasons() error {
    91  	btfSpec, err := btf.LoadKernelSpec()
    92  	if err != nil {
    93  		return fmt.Errorf("loading kernel spec: %w", err)
    94  	}
    95  
    96  	t.dropReasons = make(map[int]string)
    97  	enum := &btf.Enum{}
    98  	err = btfSpec.TypeByName("skb_drop_reason", &enum)
    99  	if err != nil {
   100  		return fmt.Errorf("looking up skb_drop_reason enum: %w", err)
   101  	}
   102  	for _, v := range enum.Values {
   103  		str := v.Name
   104  		str = strings.TrimPrefix(str, "SKB_DROP_REASON_")
   105  		str = strings.TrimPrefix(str, "SKB_")
   106  
   107  		t.dropReasons[int(v.Value)] = str
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  func (t *Tracer) install() error {
   114  	err := t.loadDropReasons()
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	spec, err := loadTcpdrop()
   120  	if err != nil {
   121  		return fmt.Errorf("loading ebpf program: %w", err)
   122  	}
   123  
   124  	gadgets.FixBpfKtimeGetBootNs(spec.Programs)
   125  
   126  	opts := ebpf.CollectionOptions{
   127  		Programs: ebpf.ProgramOptions{
   128  			KernelTypes: btfgen.GetBTFSpec(),
   129  		},
   130  	}
   131  
   132  	mapReplacements := map[string]*ebpf.Map{}
   133  	mapReplacements[socketenricher.SocketsMapName] = t.socketEnricherMap
   134  	opts.MapReplacements = mapReplacements
   135  
   136  	if err := spec.LoadAndAssign(&t.objs, &opts); err != nil {
   137  		return fmt.Errorf("loading ebpf program: %w", err)
   138  	}
   139  
   140  	t.kfreeSkbLink, err = link.Tracepoint("skb", "kfree_skb", t.objs.IgTcpdrop, nil)
   141  	if err != nil {
   142  		return fmt.Errorf("attaching tracepoint kfree_skb: %w", err)
   143  	}
   144  
   145  	reader, err := perf.NewReader(t.objs.tcpdropMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
   146  	if err != nil {
   147  		return fmt.Errorf("creating perf ring buffer: %w", err)
   148  	}
   149  	t.reader = reader
   150  
   151  	return nil
   152  }
   153  
   154  func (t *Tracer) run() {
   155  	for {
   156  		record, err := t.reader.Read()
   157  		if err != nil {
   158  			if errors.Is(err, perf.ErrClosed) {
   159  				// nothing to do, we're done
   160  				return
   161  			}
   162  
   163  			msg := fmt.Sprintf("reading perf ring buffer: %s", err)
   164  			t.eventCallback(types.Base(eventtypes.Err(msg)))
   165  			return
   166  		}
   167  
   168  		if record.LostSamples > 0 {
   169  			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
   170  			t.eventCallback(types.Base(eventtypes.Warn(msg)))
   171  			continue
   172  		}
   173  
   174  		bpfEvent := (*tcpdropEvent)(unsafe.Pointer(&record.RawSample[0]))
   175  
   176  		reason, err := t.lookupDropReason(int(bpfEvent.Reason))
   177  		if err != nil {
   178  			msg := fmt.Sprintf("looking up drop reason: %s", err)
   179  			t.eventCallback(types.Base(eventtypes.Err(msg)))
   180  			continue
   181  		}
   182  
   183  		ipversion := gadgets.IPVerFromAF(bpfEvent.Af)
   184  
   185  		event := types.Event{
   186  			Event: eventtypes.Event{
   187  				Type:      eventtypes.NORMAL,
   188  				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
   189  			},
   190  			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.ProcSocket.MountNsId},
   191  			WithNetNsID:   eventtypes.WithNetNsID{NetNsID: uint64(bpfEvent.Netns)},
   192  			Pid:           bpfEvent.ProcSocket.Pid,
   193  			Uid:           bpfEvent.ProcSocket.Uid,
   194  			Gid:           bpfEvent.ProcSocket.Gid,
   195  			Comm:          gadgets.FromCString(bpfEvent.ProcSocket.Task[:]),
   196  
   197  			SrcEndpoint: eventtypes.L4Endpoint{
   198  				L3Endpoint: eventtypes.L3Endpoint{
   199  					Addr:    gadgets.IPStringFromBytes(bpfEvent.Saddr, ipversion),
   200  					Version: uint8(ipversion),
   201  				},
   202  				Port: gadgets.Htons(bpfEvent.Sport),
   203  			},
   204  			DstEndpoint: eventtypes.L4Endpoint{
   205  				L3Endpoint: eventtypes.L3Endpoint{
   206  					Addr:    gadgets.IPStringFromBytes(bpfEvent.Daddr, ipversion),
   207  					Version: uint8(ipversion),
   208  				},
   209  				Port: gadgets.Htons(bpfEvent.Dport),
   210  			},
   211  			State:     tcpbits.TCPState(bpfEvent.State),
   212  			Tcpflags:  tcpbits.TCPFlags(bpfEvent.Tcpflags),
   213  			Reason:    reason,
   214  			IPVersion: ipversion,
   215  		}
   216  
   217  		t.eventCallback(&event)
   218  	}
   219  }
   220  
   221  func (t *Tracer) lookupDropReason(reason int) (string, error) {
   222  	if ret, ok := t.dropReasons[reason]; ok {
   223  		return ret, nil
   224  	}
   225  	return "", fmt.Errorf("unknown drop reason: %d", reason)
   226  }