inspektor-gadget

Форк
0
268 строк · 7.4 Кб
1
// Copyright 2021-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
	"fmt"
21
	"io"
22
	"unsafe"
23

24
	"github.com/cilium/ebpf"
25
	"github.com/cilium/ebpf/link"
26

27
	containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
28
	containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils"
29
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
30
	socketcollectortypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/snapshot/socket/types"
31
	"github.com/inspektor-gadget/inspektor-gadget/pkg/netnsenter"
32
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
33
)
34

35
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel -cc clang -cflags ${CFLAGS} -type entry  socket ./bpf/socket.bpf.c -- -Werror -O2 -g -c -x c
36

37
type Tracer struct {
38
	iters []*link.Iter
39

40
	// visitedNamespaces is a map where the key is the netns inode number and
41
	// the value is the pid of one of the containers that share that netns. Such
42
	// pid is used by NetnsEnter. TODO: Improve NetnsEnter to also work with the
43
	// netns directly.
44
	visitedNamespaces map[uint64]uint32
45
	protocols         socketcollectortypes.Proto
46
	eventHandler      func([]*socketcollectortypes.Event)
47
}
48

49
// Format from socket_bpf_seq_print() in bpf/socket_common.h
50
func parseStatus(proto string, statusUint uint8) (string, error) {
51
	statusMap := [...]string{
52
		"ESTABLISHED", "SYN_SENT", "SYN_RECV",
53
		"FIN_WAIT1", "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT",
54
		"LAST_ACK", "LISTEN", "CLOSING", "NEW_SYN_RECV",
55
	}
56

57
	// Kernel enum starts from 1, adjust it to the statusMap
58
	if statusUint == 0 || len(statusMap) <= int(statusUint-1) {
59
		return "", fmt.Errorf("invalid %s status: %d", proto, statusUint)
60
	}
61
	status := statusMap[statusUint-1]
62

63
	// Transform TCP status into something more suitable for UDP
64
	if proto == "UDP" {
65
		switch status {
66
		case "ESTABLISHED":
67
			status = "ACTIVE"
68
		case "CLOSE":
69
			status = "INACTIVE"
70
		default:
71
			return "", fmt.Errorf("unexpected %s status %s", proto, status)
72
		}
73
	}
74

75
	return status, nil
76
}
77

78
func (t *Tracer) runCollector(pid uint32, netns uint64) ([]*socketcollectortypes.Event, error) {
79
	sockets := []*socketcollectortypes.Event{}
80
	err := netnsenter.NetnsEnter(int(pid), func() error {
81
		for _, it := range t.iters {
82
			reader, err := it.Open()
83
			if err != nil {
84
				return fmt.Errorf("opening BPF iterator: %w", err)
85
			}
86
			defer reader.Close()
87

88
			buf, err := io.ReadAll(reader)
89
			if err != nil {
90
				return fmt.Errorf("reading BPF iterator: %w", err)
91
			}
92
			entrySize := int(unsafe.Sizeof(socketEntry{}))
93

94
			for i := 0; i < len(buf)/entrySize; i++ {
95
				entry := (*socketEntry)(unsafe.Pointer(&buf[i*entrySize]))
96

97
				proto := gadgets.ProtoString(int(entry.Proto))
98
				status, err := parseStatus(proto, entry.State)
99
				if err != nil {
100
					return err
101
				}
102

103
				ipversion := gadgets.IPVerFromAF(entry.Family)
104

105
				event := &socketcollectortypes.Event{
106
					Event: eventtypes.Event{
107
						Type: eventtypes.NORMAL,
108
					},
109
					Protocol: proto,
110
					SrcEndpoint: eventtypes.L4Endpoint{
111
						L3Endpoint: eventtypes.L3Endpoint{
112
							Addr:    gadgets.IPStringFromBytes(entry.Saddr, ipversion),
113
							Version: uint8(ipversion),
114
						},
115
						Port: entry.Sport,
116
					},
117
					DstEndpoint: eventtypes.L4Endpoint{
118
						L3Endpoint: eventtypes.L3Endpoint{
119
							Addr:    gadgets.IPStringFromBytes(entry.Daddr, ipversion),
120
							Version: uint8(ipversion),
121
						},
122
						Port: entry.Dport,
123
					},
124
					Status:      status,
125
					InodeNumber: entry.Inode,
126
					WithNetNsID: eventtypes.WithNetNsID{NetNsID: netns},
127
				}
128

129
				sockets = append(sockets, event)
130
			}
131
		}
132
		return nil
133
	})
134
	if err != nil {
135
		return nil, err
136
	}
137

138
	return sockets, nil
139
}
140

141
// RunCollector is currently exported so it can be called from Collect(). It can be removed once
142
// pkg/gadget-collection/gadgets/snapshot/socket/gadget.go is gone.
143
func (t *Tracer) RunCollector(pid uint32, podname, namespace, node string) ([]*socketcollectortypes.Event, error) {
144
	netns, err := containerutils.GetNetNs(int(pid))
145
	if err != nil {
146
		return nil, fmt.Errorf("getting netns for pid %d: %w", pid, err)
147
	}
148

149
	sockets, err := t.runCollector(pid, netns)
150
	if err != nil {
151
		return nil, err
152
	}
153

154
	for _, socket := range sockets {
155
		socket.K8s.Node = node
156
		socket.K8s.Namespace = namespace
157
		socket.K8s.PodName = podname
158
	}
159

160
	return sockets, nil
161
}
162

163
// ---
164

165
func NewTracer(protocols socketcollectortypes.Proto) (*Tracer, error) {
166
	tracer := &Tracer{
167
		visitedNamespaces: make(map[uint64]uint32),
168
		protocols:         protocols,
169
	}
170

171
	if err := tracer.openIters(); err != nil {
172
		tracer.CloseIters()
173
		return nil, fmt.Errorf("installing tracer: %w", err)
174
	}
175

176
	return tracer, nil
177
}
178

179
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
180
	return &Tracer{
181
		visitedNamespaces: make(map[uint64]uint32),
182
	}, nil
183
}
184

185
func (t *Tracer) AttachContainer(container *containercollection.Container) error {
186
	if _, ok := t.visitedNamespaces[container.Netns]; ok {
187
		return nil
188
	}
189
	t.visitedNamespaces[container.Netns] = container.Pid
190
	return nil
191
}
192

193
func (t *Tracer) DetachContainer(container *containercollection.Container) error {
194
	return nil
195
}
196

197
func (t *Tracer) SetEventHandlerArray(handler any) {
198
	nh, ok := handler.(func(ev []*socketcollectortypes.Event))
199
	if !ok {
200
		panic("event handler invalid")
201
	}
202
	t.eventHandler = nh
203
}
204

205
// CloseIters is currently exported so it can be called from Collect()
206
func (t *Tracer) CloseIters() {
207
	for _, it := range t.iters {
208
		it.Close()
209
	}
210
	t.iters = nil
211
}
212

213
func (t *Tracer) openIters() error {
214
	// TODO: how to avoid loading programs that aren't needed?
215
	objs := &socketObjects{}
216
	if err := loadSocketObjects(objs, nil); err != nil {
217
		return err
218
	}
219

220
	toAttach := []*ebpf.Program{}
221

222
	switch t.protocols {
223
	case socketcollectortypes.TCP:
224
		toAttach = append(toAttach, objs.IgSnapTcp)
225
	case socketcollectortypes.UDP:
226
		toAttach = append(toAttach, objs.IgSnapUdp)
227
	case socketcollectortypes.ALL:
228
		toAttach = append(toAttach, objs.IgSnapTcp, objs.IgSnapUdp)
229
	}
230

231
	for _, prog := range toAttach {
232
		it, err := link.AttachIter(link.IterOptions{
233
			Program: prog,
234
		})
235
		if err != nil {
236
			var name string
237
			if info, err := prog.Info(); err == nil {
238
				name = info.Name
239
			}
240
			return fmt.Errorf("attaching program %q: %w", name, err)
241
		}
242
		t.iters = append(t.iters, it)
243
	}
244

245
	return nil
246
}
247

248
func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
249
	protocols := gadgetCtx.GadgetParams().Get(ParamProto).AsString()
250
	t.protocols = socketcollectortypes.ProtocolsMap[protocols]
251

252
	defer t.CloseIters()
253
	if err := t.openIters(); err != nil {
254
		return fmt.Errorf("installing tracer: %w", err)
255
	}
256

257
	allSockets := []*socketcollectortypes.Event{}
258
	for netns, pid := range t.visitedNamespaces {
259
		sockets, err := t.runCollector(pid, netns)
260
		if err != nil {
261
			return fmt.Errorf("snapshotting sockets in netns %d: %w", netns, err)
262
		}
263
		allSockets = append(allSockets, sockets...)
264
	}
265

266
	t.eventHandler(allSockets)
267
	return nil
268
}
269

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

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

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

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