github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/tcp/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/tcp/types"
    32  	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    33  )
    34  
    35  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types -type event -type event_type tcptracer ./bpf/tcptracer.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 tcptracerObjects
    47  
    48  	tcpv4connectEnterLink link.Link
    49  	tcpv4connectExitLink  link.Link
    50  	tcpv6connectEnterLink link.Link
    51  	tcpv6connectExitLink  link.Link
    52  	tcpCloseEnterLink     link.Link
    53  	tcpSetStateEnterLink  link.Link
    54  	inetCskAcceptExitLink link.Link
    55  
    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.tcpv4connectEnterLink = gadgets.CloseLink(t.tcpv4connectEnterLink)
    86  	t.tcpv4connectExitLink = gadgets.CloseLink(t.tcpv4connectExitLink)
    87  	t.tcpv6connectEnterLink = gadgets.CloseLink(t.tcpv6connectEnterLink)
    88  	t.tcpv6connectExitLink = gadgets.CloseLink(t.tcpv6connectExitLink)
    89  	t.tcpCloseEnterLink = gadgets.CloseLink(t.tcpCloseEnterLink)
    90  	t.tcpSetStateEnterLink = gadgets.CloseLink(t.tcpSetStateEnterLink)
    91  	t.inetCskAcceptExitLink = gadgets.CloseLink(t.inetCskAcceptExitLink)
    92  
    93  	if t.reader != nil {
    94  		t.reader.Close()
    95  	}
    96  
    97  	t.objs.Close()
    98  }
    99  
   100  func (t *Tracer) install() error {
   101  	spec, err := loadTcptracer()
   102  	if err != nil {
   103  		return fmt.Errorf("loading ebpf program: %w", err)
   104  	}
   105  
   106  	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, nil, &t.objs); err != nil {
   107  		return fmt.Errorf("loading ebpf spec: %w", err)
   108  	}
   109  
   110  	t.tcpv4connectEnterLink, err = link.Kprobe("tcp_v4_connect", t.objs.IgTcpV4CoE, nil)
   111  	if err != nil {
   112  		return fmt.Errorf("attaching kprobe: %w", err)
   113  	}
   114  
   115  	t.tcpv4connectExitLink, err = link.Kretprobe("tcp_v4_connect", t.objs.IgTcpV4CoX, nil)
   116  	if err != nil {
   117  		return fmt.Errorf("attaching kprobe: %w", err)
   118  	}
   119  
   120  	t.tcpv6connectEnterLink, err = link.Kprobe("tcp_v6_connect", t.objs.IgTcpV6CoE, nil)
   121  	if err != nil {
   122  		return fmt.Errorf("attaching kprobe: %w", err)
   123  	}
   124  
   125  	t.tcpv6connectExitLink, err = link.Kretprobe("tcp_v6_connect", t.objs.IgTcpV6CoX, nil)
   126  	if err != nil {
   127  		return fmt.Errorf("attaching kprobe: %w", err)
   128  	}
   129  
   130  	// TODO: rename function in ebpf program
   131  	t.tcpCloseEnterLink, err = link.Kprobe("tcp_close", t.objs.IgTcpClose, nil)
   132  	if err != nil {
   133  		return fmt.Errorf("attaching kprobe: %w", err)
   134  	}
   135  
   136  	t.tcpSetStateEnterLink, err = link.Kprobe("tcp_set_state", t.objs.IgTcpState, nil)
   137  	if err != nil {
   138  		return fmt.Errorf("attaching kprobe: %w", err)
   139  	}
   140  
   141  	t.inetCskAcceptExitLink, err = link.Kretprobe("inet_csk_accept", t.objs.IgTcpAccept, nil)
   142  	if err != nil {
   143  		return fmt.Errorf("attaching kprobe: %w", err)
   144  	}
   145  
   146  	reader, err := perf.NewReader(t.objs.tcptracerMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
   147  	if err != nil {
   148  		return fmt.Errorf("creating perf ring buffer: %w", err)
   149  	}
   150  	t.reader = reader
   151  
   152  	return nil
   153  }
   154  
   155  func (t *Tracer) run() {
   156  	for {
   157  		record, err := t.reader.Read()
   158  		if err != nil {
   159  			if errors.Is(err, perf.ErrClosed) {
   160  				// nothing to do, we're done
   161  				return
   162  			}
   163  
   164  			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
   165  			t.eventCallback(types.Base(eventtypes.Err(msg)))
   166  			return
   167  		}
   168  
   169  		if record.LostSamples > 0 {
   170  			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
   171  			t.eventCallback(types.Base(eventtypes.Warn(msg)))
   172  			continue
   173  		}
   174  
   175  		bpfEvent := (*tcptracerEvent)(unsafe.Pointer(&record.RawSample[0]))
   176  
   177  		ipversion := gadgets.IPVerFromAF(bpfEvent.Af)
   178  
   179  		event := types.Event{
   180  			Event: eventtypes.Event{
   181  				Type:      eventtypes.NORMAL,
   182  				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
   183  			},
   184  			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId},
   185  			Pid:           bpfEvent.Pid,
   186  			Uid:           bpfEvent.Uid,
   187  			Gid:           bpfEvent.Gid,
   188  			Comm:          gadgets.FromCString(bpfEvent.Task[:]),
   189  			SrcEndpoint: eventtypes.L4Endpoint{
   190  				L3Endpoint: eventtypes.L3Endpoint{
   191  					Addr:    gadgets.IPStringFromBytes(bpfEvent.Saddr, ipversion),
   192  					Version: uint8(ipversion),
   193  				},
   194  				Port: gadgets.Htons(bpfEvent.Sport),
   195  			},
   196  			DstEndpoint: eventtypes.L4Endpoint{
   197  				L3Endpoint: eventtypes.L3Endpoint{
   198  					Addr:    gadgets.IPStringFromBytes(bpfEvent.Daddr, ipversion),
   199  					Version: uint8(ipversion),
   200  				},
   201  				Port: gadgets.Htons(bpfEvent.Dport),
   202  			},
   203  			IPVersion: ipversion,
   204  		}
   205  
   206  		switch bpfEvent.Type {
   207  		case tcptracerEventTypeTCP_EVENT_TYPE_CONNECT:
   208  			event.Operation = "connect"
   209  		case tcptracerEventTypeTCP_EVENT_TYPE_ACCEPT:
   210  			event.Operation = "accept"
   211  		case tcptracerEventTypeTCP_EVENT_TYPE_CLOSE:
   212  			event.Operation = "close"
   213  		}
   214  
   215  		if t.enricher != nil {
   216  			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
   217  		}
   218  
   219  		t.eventCallback(&event)
   220  	}
   221  }
   222  
   223  // --- Registry changes
   224  
   225  func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
   226  	defer t.close()
   227  	if err := t.install(); err != nil {
   228  		return fmt.Errorf("installing tracer: %w", err)
   229  	}
   230  
   231  	go t.run()
   232  	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
   233  
   234  	return nil
   235  }
   236  
   237  func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
   238  	t.config.MountnsMap = mountnsMap
   239  }
   240  
   241  func (t *Tracer) SetEventHandler(handler any) {
   242  	nh, ok := handler.(func(ev *types.Event))
   243  	if !ok {
   244  		panic("event handler invalid")
   245  	}
   246  	t.eventCallback = nh
   247  }
   248  
   249  func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
   250  	tracer := &Tracer{
   251  		config: &Config{},
   252  	}
   253  	return tracer, nil
   254  }