...
1 package features
2
3 import (
4 "errors"
5 "fmt"
6 "os"
7 "sync"
8 "unsafe"
9
10 "github.com/cilium/ebpf"
11 "github.com/cilium/ebpf/internal/sys"
12 "github.com/cilium/ebpf/internal/unix"
13 )
14
15 func init() {
16 mc.mapTypes = make(map[ebpf.MapType]error)
17 }
18
19 var (
20 mc mapCache
21 )
22
23 type mapCache struct {
24 sync.Mutex
25 mapTypes map[ebpf.MapType]error
26 }
27
28 func createMapTypeAttr(mt ebpf.MapType) *sys.MapCreateAttr {
29 var (
30 keySize uint32 = 4
31 valueSize uint32 = 4
32 maxEntries uint32 = 1
33 innerMapFd uint32
34 flags uint32
35 btfKeyTypeID uint32
36 btfValueTypeID uint32
37 btfFd uint32
38 )
39
40
41 switch mt {
42 case ebpf.StackTrace:
43
44 valueSize = 8
45 case ebpf.LPMTrie:
46
47
48
49 keySize = 8
50 valueSize = 8
51 flags = unix.BPF_F_NO_PREALLOC
52 case ebpf.ArrayOfMaps, ebpf.HashOfMaps:
53
54
55 innerMapFd = ^uint32(0)
56 case ebpf.CGroupStorage, ebpf.PerCPUCGroupStorage:
57
58
59
60 var align int
61 keySize = uint32(8 + unsafe.Sizeof(align))
62 maxEntries = 0
63 case ebpf.Queue, ebpf.Stack:
64
65 keySize = 0
66 case ebpf.RingBuf:
67
68
69
70 keySize = 0
71 valueSize = 0
72 maxEntries = uint32(os.Getpagesize())
73 case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage:
74
75
76
77
78 maxEntries = 0
79 flags = unix.BPF_F_NO_PREALLOC
80 btfKeyTypeID = 1
81 btfValueTypeID = 3
82 btfFd = ^uint32(0)
83 }
84
85 return &sys.MapCreateAttr{
86 MapType: sys.MapType(mt),
87 KeySize: keySize,
88 ValueSize: valueSize,
89 MaxEntries: maxEntries,
90 InnerMapFd: innerMapFd,
91 MapFlags: flags,
92 BtfKeyTypeId: btfKeyTypeID,
93 BtfValueTypeId: btfValueTypeID,
94 BtfFd: btfFd,
95 }
96 }
97
98
99
100
101 func HaveMapType(mt ebpf.MapType) error {
102 if err := validateMaptype(mt); err != nil {
103 return err
104 }
105
106 return haveMapType(mt)
107 }
108
109 func validateMaptype(mt ebpf.MapType) error {
110 if mt > mt.Max() {
111 return os.ErrInvalid
112 }
113
114 if mt == ebpf.StructOpsMap {
115
116
117
118 return errors.New("a probe for MapType StructOpsMap isn't implemented")
119 }
120
121 return nil
122 }
123
124 func haveMapType(mt ebpf.MapType) error {
125 mc.Lock()
126 defer mc.Unlock()
127 err, ok := mc.mapTypes[mt]
128 if ok {
129 return err
130 }
131
132 fd, err := sys.MapCreate(createMapTypeAttr(mt))
133
134 switch {
135
136 case errors.Is(err, unix.EBADF):
137 if isMapOfMaps(mt) || isStorageMap(mt) {
138 err = nil
139 }
140
141
142
143
144
145 case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
146 err = ebpf.ErrNotSupported
147
148
149 case errors.Is(err, unix.EPERM):
150 break
151
152
153 case err != nil:
154 err = fmt.Errorf("unexpected error during feature probe: %w", err)
155
156 default:
157 fd.Close()
158 }
159
160 mc.mapTypes[mt] = err
161
162 return err
163 }
164
165 func isMapOfMaps(mt ebpf.MapType) bool {
166 switch mt {
167 case ebpf.ArrayOfMaps, ebpf.HashOfMaps:
168 return true
169 }
170 return false
171 }
172
173 func isStorageMap(mt ebpf.MapType) bool {
174 switch mt {
175 case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage:
176 return true
177 }
178
179 return false
180 }
181
View as plain text