-
Notifications
You must be signed in to change notification settings - Fork 2
/
map.go
386 lines (344 loc) · 10.2 KB
/
map.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
// Copyright 2018 Andrei Tudor Călin
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package ebpf
// Low level map routines.
import (
"errors"
"fmt"
"unsafe"
"golang.org/x/sys/unix"
)
// MapType is the type of an eBPF map.
type MapType uint32
// Supported map types.
const (
MapUnspec MapType = iota
MapHash
MapArray
MapProgArray
MapPerfEventArray
MapPerCPUHash
MapPerCPUArray
MapStackTrace
MapCGroupArray
MapLRUHash
MapLRUPerCPUHash
MapLPMTrie
MapArrayOfMaps
MapHashOfMaps
MapDevmap
MapSockmap
MapCPUmap
MapXSKMap
MapSockhash
MapCGroupStorage
MapReuseportSockarray
maxMapType // keep at the end
)
// Map configures an eBPF map.
//
// Before use, a Map must be initialized using the Init method. KeySize,
// and MaxEntries must be non-zero. For arrays, KeySize must be 4.
//
// TODO(acln): investigate if ValueSize can be zero.
//
// ObjectName names the map. Names must not contain the NULL character. Names
// longer than 15 bytes are truncated.
//
// A Map must not be copied after initialization. After initialization,
// it is safe (in the data race sense) to call methods on the Map from
// multiple goroutines concurrently. However, it may not always be safe to
// do so from the perspective of the actual eBPF map semantics. For example,
// writes to hash maps are atomic, while writes to arrays are not. Consult
// the bpf documentation for more details.
//
// TODO(acln): eitehr expand on where to find said documentation, or document
// the semantics more precisely.
type Map struct {
Type MapType
KeySize uint32
ValueSize uint32
MaxEntries uint32
Flags uint32 // TODO(acln): investigate these
InnerMap *Map // TODO(acln): is this right?
NUMANode uint32
ObjectName string
InterfaceIndex uint32 // TODO(acln): document this
// TODO(acln): add the BTF bits at some point
// fd is the inner map FD. It is a pointer, to prevent callers
// from copying a Map and sweeping the actual FD from under us.
fd *mapFD
}
// Init initializes the map.
func (m *Map) Init() error {
if m.Type == MapUnspec {
return errors.New("ebpf: unspecified map type")
}
if m.Type >= maxMapType {
return fmt.Errorf("ebpf: invalid map type %d", m.Type)
}
if m.KeySize == 0 {
return errors.New("ebpf: map KeySize not configured")
}
if m.MaxEntries == 0 {
return errors.New("ebpf: map MaxEntries not configured")
}
cfg := mapAttr{
Type: m.Type,
KeySize: m.KeySize,
ValueSize: m.ValueSize,
MaxEntries: m.MaxEntries,
Flags: m.Flags,
InnerMapFD: 0, // TODO(acln): fix this
NUMANode: m.NUMANode,
Name: newObjectName(m.ObjectName),
InterfaceIndex: m.InterfaceIndex,
}
fd := new(mapFD)
if err := fd.Init(&cfg); err != nil {
return wrapMapOpError("init", err)
}
m.fd = fd
return nil
}
var errUninitializedMap = errors.New("ebpf: use of uninitialized map")
// Lookup looks up the value for k and stores it in v. If k is not found
// in the map, Lookup returns an error such that IsNotExist(err) == true.
func (m *Map) Lookup(k, v []byte) error {
if m.fd == nil {
return errUninitializedMap
}
return wrapMapOpError("lookup", m.fd.Lookup(k, v))
}
// Set sets the value for k to v. If an entry for k exists in the map,
// it will be overwritten.
func (m *Map) Set(k, v []byte) error {
if m.fd == nil {
return errUninitializedMap
}
return wrapMapOpError("set", m.fd.Update(k, v, mapUpdateAny))
}
// Create creates a new entry for k in the map, and sets the value to v.
// If an entry for k exists in the map, Create returns an error such that
// IsExist(err) == true.
func (m *Map) Create(k, v []byte) error {
if m.fd == nil {
return errUninitializedMap
}
return wrapMapOpError("create", m.fd.Update(k, v, mapUpdateNoexist))
}
// Update updates the entry for k to v. If an entry for k does not exist in
// the map, Update returns an error such that IsNotExist(err) == true.
func (m *Map) Update(k, v []byte) error {
if m.fd == nil {
return errUninitializedMap
}
return wrapMapOpError("update", m.fd.Update(k, v, mapUpdateExist))
}
// DeleteElem deletes the entry for k. If an entry for k does not exist in
// the map, DeleteElem returns an error such that IsNotExist(err) == true.
func (m *Map) DeleteElem(k []byte) error {
if m.fd == nil {
return errUninitializedMap
}
return wrapMapOpError("delete", m.fd.DeleteElem(k))
}
// MapIterFunc is a map iterator function.
type MapIterFunc func(key, value []byte) (stop bool)
// Iter iterates over all keys in the map and calls fn for each key-value
// pair. If fn returns true or the final element of the map is reached,
// iteration stops. fn must not retain the arguments it is called with.
//
// startHint optionally specifies a key that does *not* exist in the map, such
// that Iterate can begin iteration from the first key that does. Due to the
// nature of BPF map iterators, on Linux kernels older than 4.12, Iterate
// requires a non-nil startHint. On Linux >= 4.12, startHint may be nil, but
// it is recommended to pass a valid one nevertheless.
func (m *Map) Iter(fn MapIterFunc, startHint []byte) error {
if m.fd == nil {
return errUninitializedMap
}
return wrapMapOpError("iter", m.fd.Iter(fn, startHint))
}
// Close destroys the map and releases the associated file descriptor. After a call
// to Close, future method calls on the Map will return errors.
func (m *Map) Close() error {
if m.fd == nil {
return errUninitializedMap
}
return m.fd.Close()
}
// readFD stores the underlying file descriptor into fd.
func (m *Map) readFD() (int, error) {
if m.fd == nil {
return -1, errUninitializedMap
}
return m.fd.RawFD()
}
// mapFD is a low level wrapper around a bpf map file descriptor.
type mapFD struct {
bfd bpfFD
keySize int
valueSize int
maxEntries uint32
}
func (m *mapFD) Init(cfg *mapAttr) error {
rawfd, err := createMap(cfg)
if err != nil {
return wrapCmdError(cmdMapCreate, err)
}
if err := m.bfd.Init(rawfd, unix.Close); err != nil {
return err
}
m.keySize = int(cfg.KeySize)
m.valueSize = int(cfg.ValueSize)
m.maxEntries = cfg.MaxEntries
return nil
}
func (m *mapFD) Lookup(k, v []byte) error {
if err := m.ensureCorrectKeyValueSize(k, v); err != nil {
return err
}
return m.bfd.MapLookup(k, v)
}
// Flags for map update operations.
const (
mapUpdateAny = iota // BPF_UPDATE_ANY
mapUpdateNoexist // BPF_UPDATE_NOEXIST
mapUpdateExist // BPF_UPDATE_EXIST
)
func (m *mapFD) Update(k, v []byte, flag uint64) error {
if err := m.ensureCorrectKeyValueSize(k, v); err != nil {
return err
}
return m.bfd.MapUpdate(k, v, flag)
}
func (m *mapFD) DeleteElem(k []byte) error {
if err := m.ensureCorrectKeySize(k); err != nil {
return err
}
return m.bfd.MapDeleteElem(k)
}
func (m *mapFD) Iter(fn MapIterFunc, startHint []byte) error {
key := make([]byte, m.keySize)
if err := m.bfd.FindFirstMapKey(startHint, key); err != nil {
return err
}
nextKey := make([]byte, m.keySize)
value := make([]byte, m.valueSize)
for {
if err := m.bfd.MapLookup(key, value); err != nil {
return err
}
if stop := fn(key, value); stop {
return nil
}
if err := m.bfd.MapGetNextKeyNoWrap(key, nextKey); err != nil {
if err == unix.ENOENT {
// No more entries. Clean end.
return nil
}
return wrapCmdError(cmdMapGetNextKey, err)
}
copy(key, nextKey)
}
}
func (m *mapFD) RawFD() (int, error) {
return m.bfd.RawFD()
}
func (m *mapFD) Close() error {
return m.bfd.Close()
}
// argumentSizeError records an error a mismatch between the size of a key
// or value argument to a map operation, and the key or value size the map
// was configured with.
//
// argumentSizeError is a programmer error, but it exists as an explicit
// type because showing the caller an EINVAL that came from the kernel
// might not be illuminating enough. The type is nevertheless unexported,
// because this error is not actionable for callers.
type argumentSizeError struct {
arg string
got int
want int
}
func (e *argumentSizeError) Error() string {
return fmt.Sprintf("%s size is %d, want %d", e.arg, e.got, e.want)
}
func (m *mapFD) ensureCorrectKeyValueSize(k, v []byte) error {
if err := m.ensureCorrectKeySize(k); err != nil {
return err
}
if len(v) != m.valueSize {
return &argumentSizeError{
arg: "value",
got: len(v),
want: m.valueSize,
}
}
return nil
}
func (m *mapFD) ensureCorrectKeySize(k []byte) error {
if len(k) != m.keySize {
return &argumentSizeError{
arg: "key",
got: len(k),
want: m.keySize,
}
}
return nil
}
// createMap creates a new BPF map.
func createMap(attr *mapAttr) (rawfd int, err error) {
return bpf(cmdMapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
}
// mapAttr holds attributes which configure a map, for a BPF_MAP_CREATE
// operation.
type mapAttr struct {
Type MapType
KeySize uint32
ValueSize uint32
MaxEntries uint32
Flags uint32
InnerMapFD uint32
NUMANode uint32
Name objectName
InterfaceIndex uint32
BTFFD uint32
BTFKeyTypeID uint32
BTFValueTypeID uint32
}
// MapOpError records an error caused by a map operation.
//
// Op is the high level operation performed.
//
// In some cases, Err is of type SyscallError.
type MapOpError struct {
Op string
Err error
}
func (e *MapOpError) Error() string {
return fmt.Sprintf("ebpf: map %s: %v", e.Op, e.Err)
}
// Unwrap returns e.Err.
func (e *MapOpError) Unwrap() error {
return e.Err
}
// wrapMapOpError wraps err in a *MapOpError. Returns nil if err == nil.
func wrapMapOpError(op string, err error) error {
if err == nil {
return nil
}
return &MapOpError{Op: op, Err: err}
}