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

     1  // Copyright 2022-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  	"time"
    24  	"unsafe"
    25  
    26  	"github.com/cilium/ebpf"
    27  	"github.com/cilium/ebpf/link"
    28  	"github.com/cilium/ebpf/perf"
    29  
    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/tcpconnect/types"
    33  	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    34  )
    35  
    36  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -type event tcpconnect ./bpf/tcpconnect.bpf.c -- -I./bpf/
    37  
    38  type Config struct {
    39  	MountnsMap       *ebpf.Map
    40  	CalculateLatency bool
    41  	MinLatency       time.Duration
    42  }
    43  
    44  type Tracer struct {
    45  	config        *Config
    46  	enricher      gadgets.DataEnricherByMntNs
    47  	eventCallback func(*types.Event)
    48  
    49  	objs                   tcpconnectObjects
    50  	v4EnterLink            link.Link
    51  	v4ExitLink             link.Link
    52  	v6EnterLink            link.Link
    53  	v6ExitLink             link.Link
    54  	tcpDestroySockLink     link.Link
    55  	tcpRvcStateProcessLink link.Link
    56  	reader                 *perf.Reader
    57  }
    58  
    59  func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
    60  	eventCallback func(*types.Event),
    61  ) (*Tracer, error) {
    62  	t := &Tracer{
    63  		config:        config,
    64  		enricher:      enricher,
    65  		eventCallback: eventCallback,
    66  	}
    67  
    68  	if err := t.install(); err != nil {
    69  		t.close()
    70  		return nil, err
    71  	}
    72  
    73  	go t.run()
    74  
    75  	return t, nil
    76  }
    77  
    78  // Stop stops the tracer
    79  // TODO: Remove after refactoring
    80  func (t *Tracer) Stop() {
    81  	t.close()
    82  }
    83  
    84  func (t *Tracer) close() {
    85  	t.v4EnterLink = gadgets.CloseLink(t.v4EnterLink)
    86  	t.v4ExitLink = gadgets.CloseLink(t.v4ExitLink)
    87  	t.v6EnterLink = gadgets.CloseLink(t.v6EnterLink)
    88  	t.v6ExitLink = gadgets.CloseLink(t.v6ExitLink)
    89  	t.tcpDestroySockLink = gadgets.CloseLink(t.tcpDestroySockLink)
    90  	t.tcpRvcStateProcessLink = gadgets.CloseLink(t.tcpRvcStateProcessLink)
    91  
    92  	t.objs.Close()
    93  }
    94  
    95  func (t *Tracer) install() error {
    96  	var err error
    97  	spec, err := loadTcpconnect()
    98  	if err != nil {
    99  		return fmt.Errorf("loading ebpf program: %w", err)
   100  	}
   101  
   102  	consts := map[string]interface{}{
   103  		"targ_min_latency_ns": t.config.MinLatency,
   104  		"calculate_latency":   t.config.CalculateLatency,
   105  	}
   106  
   107  	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
   108  		return fmt.Errorf("loading ebpf spec: %w", err)
   109  	}
   110  
   111  	t.v4EnterLink, err = link.Kprobe("tcp_v4_connect", t.objs.IgTcpcV4CoE, nil)
   112  	if err != nil {
   113  		return fmt.Errorf("attaching kprobe: %w", err)
   114  	}
   115  
   116  	t.v6EnterLink, err = link.Kprobe("tcp_v6_connect", t.objs.IgTcpcV6CoE, nil)
   117  	if err != nil {
   118  		return fmt.Errorf("attaching kprobe: %w", err)
   119  	}
   120  
   121  	if !t.config.CalculateLatency {
   122  		t.v4ExitLink, err = link.Kretprobe("tcp_v4_connect", t.objs.IgTcpcV4CoX, nil)
   123  		if err != nil {
   124  			return fmt.Errorf("attaching kretprobe: %w", err)
   125  		}
   126  
   127  		t.v6ExitLink, err = link.Kretprobe("tcp_v6_connect", t.objs.IgTcpcV6CoX, nil)
   128  		if err != nil {
   129  			return fmt.Errorf("attaching kretprobe: %w", err)
   130  		}
   131  	} else {
   132  		t.tcpDestroySockLink, err = link.Tracepoint("tcp", "tcp_destroy_sock", t.objs.IgTcpDestroy, nil)
   133  		if err != nil {
   134  			return fmt.Errorf("attaching tracepoint: %w", err)
   135  		}
   136  
   137  		t.tcpRvcStateProcessLink, err = link.Kprobe("tcp_rcv_state_process", t.objs.IgTcpRsp, nil)
   138  		if err != nil {
   139  			return fmt.Errorf("attaching kprobe: %w", err)
   140  		}
   141  	}
   142  
   143  	reader, err := perf.NewReader(t.objs.tcpconnectMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
   144  	if err != nil {
   145  		return fmt.Errorf("creating perf ring buffer: %w", err)
   146  	}
   147  	t.reader = reader
   148  
   149  	return nil
   150  }
   151  
   152  func (t *Tracer) run() {
   153  	for {
   154  		record, err := t.reader.Read()
   155  		if err != nil {
   156  			if errors.Is(err, perf.ErrClosed) {
   157  				// nothing to do, we're done
   158  				return
   159  			}
   160  
   161  			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
   162  			t.eventCallback(types.Base(eventtypes.Err(msg)))
   163  			return
   164  		}
   165  
   166  		if record.LostSamples > 0 {
   167  			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
   168  			t.eventCallback(types.Base(eventtypes.Warn(msg)))
   169  			continue
   170  		}
   171  
   172  		bpfEvent := (*tcpconnectEvent)(unsafe.Pointer(&record.RawSample[0]))
   173  
   174  		ipversion := gadgets.IPVerFromAF(bpfEvent.Af)
   175  
   176  		event := types.Event{
   177  			Event: eventtypes.Event{
   178  				Type:      eventtypes.NORMAL,
   179  				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
   180  			},
   181  			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId},
   182  			Pid:           bpfEvent.Pid,
   183  			Uid:           bpfEvent.Uid,
   184  			Gid:           bpfEvent.Gid,
   185  			Comm:          gadgets.FromCString(bpfEvent.Task[:]),
   186  			SrcEndpoint: eventtypes.L4Endpoint{
   187  				L3Endpoint: eventtypes.L3Endpoint{
   188  					Addr:    gadgets.IPStringFromBytes(bpfEvent.SaddrV6, ipversion),
   189  					Version: uint8(ipversion),
   190  				},
   191  				Port: gadgets.Htons(bpfEvent.Sport),
   192  			},
   193  			DstEndpoint: eventtypes.L4Endpoint{
   194  				L3Endpoint: eventtypes.L3Endpoint{
   195  					Addr:    gadgets.IPStringFromBytes(bpfEvent.DaddrV6, ipversion),
   196  					Version: uint8(ipversion),
   197  				},
   198  				Port: gadgets.Htons(bpfEvent.Dport),
   199  			},
   200  			IPVersion: ipversion,
   201  			Latency:   time.Duration(int64(bpfEvent.Latency)),
   202  		}
   203  
   204  		if t.enricher != nil {
   205  			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
   206  		}
   207  
   208  		t.eventCallback(&event)
   209  	}
   210  }
   211  
   212  // --- Registry changes
   213  
   214  func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
   215  	params := gadgetCtx.GadgetParams()
   216  	t.config.CalculateLatency = params.Get(ParamLatency).AsBool()
   217  	t.config.MinLatency = params.Get(ParamMin).AsDuration()
   218  
   219  	defer t.close()
   220  	if err := t.install(); err != nil {
   221  		return fmt.Errorf("installing tracer: %w", err)
   222  	}
   223  
   224  	go t.run()
   225  	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
   226  
   227  	return nil
   228  }
   229  
   230  func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
   231  	t.config.MountnsMap = mountnsMap
   232  }
   233  
   234  func (t *Tracer) SetEventHandler(handler any) {
   235  	nh, ok := handler.(func(ev *types.Event))
   236  	if !ok {
   237  		panic("event handler invalid")
   238  	}
   239  	t.eventCallback = nh
   240  }
   241  
   242  func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
   243  	tracer := &Tracer{
   244  		config: &Config{},
   245  	}
   246  	return tracer, nil
   247  }