inspektor-gadget

Форк
0
317 строк · 8.8 Кб
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
	"github.com/vishvananda/netlink"
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/bind/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 bind_event bindsnoop ./bpf/bindsnoop.bpf.c -- -I./bpf/
37

38
type Config struct {
39
	MountnsMap   *ebpf.Map
40
	TargetPid    int32
41
	TargetPorts  []uint16
42
	IgnoreErrors bool
43
}
44

45
type Tracer struct {
46
	config        *Config
47
	enricher      gadgets.DataEnricherByMntNs
48
	eventCallback func(*types.Event)
49

50
	objs      bindsnoopObjects
51
	ipv4Entry link.Link
52
	ipv4Exit  link.Link
53
	ipv6Entry link.Link
54
	ipv6Exit  link.Link
55
	reader    *perf.Reader
56
}
57

58
func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
59
	eventCallback func(*types.Event),
60
) (*Tracer, error) {
61
	t := &Tracer{
62
		config:        config,
63
		enricher:      enricher,
64
		eventCallback: eventCallback,
65
	}
66

67
	if err := t.install(); err != nil {
68
		t.close()
69
		return nil, err
70
	}
71

72
	go t.run()
73

74
	return t, nil
75
}
76

77
// Stop stops the tracer
78
// TODO: Remove after refactoring
79
func (t *Tracer) Stop() {
80
	t.close()
81
}
82

83
func (t *Tracer) close() {
84
	t.ipv4Entry = gadgets.CloseLink(t.ipv4Entry)
85
	t.ipv4Exit = gadgets.CloseLink(t.ipv4Exit)
86
	t.ipv6Entry = gadgets.CloseLink(t.ipv6Entry)
87
	t.ipv6Exit = gadgets.CloseLink(t.ipv6Exit)
88

89
	if t.reader != nil {
90
		t.reader.Close()
91
	}
92

93
	t.objs.Close()
94
}
95

96
func (t *Tracer) install() error {
97
	spec, err := loadBindsnoop()
98
	if err != nil {
99
		return fmt.Errorf("loading ebpf program: %w", err)
100
	}
101

102
	filterByPort := false
103
	if len(t.config.TargetPorts) > 0 {
104
		filterByPort = true
105

106
		m := spec.Maps["ports"]
107
		for _, port := range t.config.TargetPorts {
108
			m.Contents = append(m.Contents, ebpf.MapKV{Key: port, Value: port})
109
		}
110
	}
111

112
	consts := map[string]interface{}{
113
		"target_pid":     t.config.TargetPid,
114
		"filter_by_port": filterByPort,
115
		"ignore_errors":  t.config.IgnoreErrors,
116
	}
117

118
	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
119
		return fmt.Errorf("loading ebpf spec: %w", err)
120
	}
121

122
	t.ipv4Entry, err = link.Kprobe("inet_bind", t.objs.IgBindIpv4E, nil)
123
	if err != nil {
124
		return fmt.Errorf("attaching ipv4 kprobe: %w", err)
125
	}
126

127
	t.ipv4Exit, err = link.Kretprobe("inet_bind", t.objs.IgBindIpv4X, nil)
128
	if err != nil {
129
		return fmt.Errorf("attaching ipv4 kprobe: %w", err)
130
	}
131

132
	t.ipv6Entry, err = link.Kprobe("inet6_bind", t.objs.IgBindIpv6E, nil)
133
	if err != nil {
134
		return fmt.Errorf("attaching ipv6 kprobe: %w", err)
135
	}
136

137
	t.ipv6Exit, err = link.Kretprobe("inet6_bind", t.objs.IgBindIpv6X, nil)
138
	if err != nil {
139
		return fmt.Errorf("attaching ipv6 kprobe: %w", err)
140
	}
141

142
	t.reader, err = perf.NewReader(t.objs.bindsnoopMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
143
	if err != nil {
144
		return fmt.Errorf("creating perf ring buffer: %w", err)
145
	}
146

147
	return nil
148
}
149

150
// optionsToString translates options bitfield to a string containing a letter
151
// if the option is set or a dot.
152
// It is a translation of opts2array added in this commit of kinvolk/bcc:
153
// 9621f010e33c ("tools/bindsnoop: add support for --json")
154
func optionsToString(options uint8) string {
155
	ret := ""
156
	bit := uint8(1)
157

158
	for _, option := range []string{"F", "T", "N", "R", "r"} {
159
		if (options & bit) != 0 {
160
			ret = option + ret
161
		} else {
162
			ret = "." + ret
163
		}
164
		bit <<= 1
165
	}
166

167
	return ret
168
}
169

170
// Taken from:
171
// https://elixir.bootlin.com/linux/v5.16.10/source/include/uapi/linux/in.h#L28
172
var socketProtocol = map[uint16]string{
173
	0:   "IP",       // Dummy protocol for TCP
174
	1:   "ICMP",     // Internet Control Message Protocol
175
	2:   "IGMP",     // Internet Group Management Protocol
176
	4:   "IPIP",     // IPIP tunnels (older KA9Q tunnels use 94)
177
	6:   "TCP",      // Transmission Control Protocol
178
	8:   "EGP",      // Exterior Gateway Protocol
179
	12:  "PUP",      // PUP protocol
180
	17:  "UDP",      // User Datagram Protocol
181
	22:  "IDP",      // XNS IDP protocol
182
	29:  "TP",       // SO Transport Protocol Class 4
183
	33:  "DCCP",     // Datagram Congestion Control Protocol
184
	41:  "IPV6",     // IPv6-in-IPv4 tunnelling
185
	46:  "RSVP",     // RSVP Protocol
186
	47:  "GRE",      // Cisco GRE tunnels (rfc 1701,1702)
187
	50:  "ESP",      // Encapsulation Security Payload protocol
188
	51:  "AH",       // Authentication Header protocol
189
	92:  "MTP",      // Multicast Transport Protocol
190
	94:  "BEETPH",   // IP option pseudo header for BEET
191
	98:  "ENCAP",    // Encapsulation Header
192
	103: "PIM",      // Protocol Independent Multicast
193
	108: "COMP",     // Compression Header Protocol
194
	132: "SCTP",     // Stream Control Transport Protocol
195
	136: "UDPLITE",  // UDP-Lite (RFC 3828)
196
	137: "MPLS",     // MPLS in IP (RFC 4023)
197
	143: "ETHERNET", // Ethernet-within-IPv6 Encapsulation
198
	255: "RAW",      // Raw IP packets
199
	262: "MPTCP",    // Multipath TCP connection
200
}
201

202
// protocolToString translates a kernel protocol enum value to the protocol
203
// name.
204
func protocolToString(protocol uint16) string {
205
	protocolString, ok := socketProtocol[protocol]
206
	if !ok {
207
		protocolString = "UNKNOWN"
208
	}
209

210
	return protocolString
211
}
212

213
func (t *Tracer) run() {
214
	for {
215
		record, err := t.reader.Read()
216
		if err != nil {
217
			if errors.Is(err, perf.ErrClosed) {
218
				// nothing to do, we're done
219
				return
220
			}
221

222
			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
223
			t.eventCallback(types.Base(eventtypes.Err(msg)))
224
			return
225
		}
226

227
		if record.LostSamples > 0 {
228
			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
229
			t.eventCallback(types.Base(eventtypes.Warn(msg)))
230
			continue
231
		}
232

233
		bpfEvent := (*bindsnoopBindEvent)(unsafe.Pointer(&record.RawSample[0]))
234

235
		interfaceString := ""
236
		interfaceNum := int(bpfEvent.BoundDevIf)
237
		if interfaceNum != 0 {
238
			// It does exist a net link which index is 0.
239
			// But eBPF bindsnoop code often gives 0 as interface number:
240
			// https://github.com/iovisor/bcc/blob/63618552f81a2631990eff59fd7460802c58c30b/tools/bindsnoop_example.txt#L16
241
			// So, we only use this function if interface number is different than 0.
242
			interf, err := netlink.LinkByIndex(interfaceNum)
243
			if err != nil {
244
				msg := fmt.Sprintf("Cannot get net interface for %d : %s", interfaceNum, err)
245
				t.eventCallback(types.Base(eventtypes.Err(msg)))
246
				return
247
			}
248

249
			interfaceString = interf.Attrs().Name
250
		}
251

252
		addr := gadgets.IPStringFromBytes(bpfEvent.Addr, int(bpfEvent.Ver))
253

254
		event := types.Event{
255
			Event: eventtypes.Event{
256
				Type:      eventtypes.NORMAL,
257
				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
258
			},
259
			Pid:           bpfEvent.Pid,
260
			Protocol:      protocolToString(bpfEvent.Proto),
261
			Addr:          addr,
262
			Port:          bpfEvent.Port,
263
			Options:       optionsToString(bpfEvent.Opts),
264
			Interface:     interfaceString,
265
			Comm:          gadgets.FromCString(bpfEvent.Task[:]),
266
			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MountNsId},
267
			Uid:           bpfEvent.Uid,
268
			Gid:           bpfEvent.Gid,
269
		}
270

271
		if t.enricher != nil {
272
			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
273
		}
274

275
		t.eventCallback(&event)
276
	}
277
}
278

279
// --- Registry changes
280

281
func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
282
	params := gadgetCtx.GadgetParams()
283
	t.config.TargetPid = params.Get(ParamPID).AsInt32()
284
	t.config.TargetPorts = params.Get(ParamPorts).AsUint16Slice()
285
	t.config.IgnoreErrors = params.Get(ParamIgnoreErrors).AsBool()
286

287
	defer t.close()
288
	if err := t.install(); err != nil {
289
		return fmt.Errorf("installing tracer: %w", err)
290
	}
291

292
	// TODO: Rework this to be able to stop the gadget when an error occurs in
293
	// run(). Notice it is the same for most of gadgets in the trace category.
294
	go t.run()
295
	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
296

297
	return nil
298
}
299

300
func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
301
	t.config.MountnsMap = mountnsMap
302
}
303

304
func (t *Tracer) SetEventHandler(handler any) {
305
	nh, ok := handler.(func(ev *types.Event))
306
	if !ok {
307
		panic("event handler invalid")
308
	}
309
	t.eventCallback = nh
310
}
311

312
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
313
	tracer := &Tracer{
314
		config: &Config{},
315
	}
316
	return tracer, nil
317
}
318

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.