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