1 package ebpf
2
3 import (
4 "bytes"
5 "errors"
6 "fmt"
7
8 "github.com/cilium/ebpf/asm"
9 "github.com/cilium/ebpf/internal"
10 "github.com/cilium/ebpf/internal/sys"
11 "github.com/cilium/ebpf/internal/unix"
12 )
13
14
15
16 func invalidBPFObjNameChar(char rune) bool {
17 dotAllowed := objNameAllowsDot() == nil
18
19 switch {
20 case char >= 'A' && char <= 'Z':
21 return false
22 case char >= 'a' && char <= 'z':
23 return false
24 case char >= '0' && char <= '9':
25 return false
26 case dotAllowed && char == '.':
27 return false
28 case char == '_':
29 return false
30 default:
31 return true
32 }
33 }
34
35 func progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, error) {
36 buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
37 if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
38 return nil, err
39 }
40 bytecode := buf.Bytes()
41
42 return sys.ProgLoad(&sys.ProgLoadAttr{
43 ProgType: sys.ProgType(typ),
44 License: sys.NewStringPointer(license),
45 Insns: sys.NewSlicePointer(bytecode),
46 InsnCnt: uint32(len(bytecode) / asm.InstructionSize),
47 })
48 }
49
50 var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() error {
51 _, err := sys.MapCreate(&sys.MapCreateAttr{
52 MapType: sys.MapType(ArrayOfMaps),
53 KeySize: 4,
54 ValueSize: 4,
55 MaxEntries: 1,
56
57 InnerMapFd: ^uint32(0),
58 })
59 if errors.Is(err, unix.EINVAL) {
60 return internal.ErrNotSupported
61 }
62 if errors.Is(err, unix.EBADF) {
63 return nil
64 }
65 return err
66 })
67
68 var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() error {
69
70
71 m, err := sys.MapCreate(&sys.MapCreateAttr{
72 MapType: sys.MapType(Array),
73 KeySize: 4,
74 ValueSize: 4,
75 MaxEntries: 1,
76 MapFlags: unix.BPF_F_RDONLY_PROG,
77 })
78 if err != nil {
79 return internal.ErrNotSupported
80 }
81 _ = m.Close()
82 return nil
83 })
84
85 var haveMmapableMaps = internal.FeatureTest("mmapable maps", "5.5", func() error {
86
87 m, err := sys.MapCreate(&sys.MapCreateAttr{
88 MapType: sys.MapType(Array),
89 KeySize: 4,
90 ValueSize: 4,
91 MaxEntries: 1,
92 MapFlags: unix.BPF_F_MMAPABLE,
93 })
94 if err != nil {
95 return internal.ErrNotSupported
96 }
97 _ = m.Close()
98 return nil
99 })
100
101 var haveInnerMaps = internal.FeatureTest("inner maps", "5.10", func() error {
102
103 m, err := sys.MapCreate(&sys.MapCreateAttr{
104 MapType: sys.MapType(Array),
105 KeySize: 4,
106 ValueSize: 4,
107 MaxEntries: 1,
108 MapFlags: unix.BPF_F_INNER_MAP,
109 })
110 if err != nil {
111 return internal.ErrNotSupported
112 }
113 _ = m.Close()
114 return nil
115 })
116
117 var haveNoPreallocMaps = internal.FeatureTest("prealloc maps", "4.6", func() error {
118
119 m, err := sys.MapCreate(&sys.MapCreateAttr{
120 MapType: sys.MapType(Hash),
121 KeySize: 4,
122 ValueSize: 4,
123 MaxEntries: 1,
124 MapFlags: unix.BPF_F_NO_PREALLOC,
125 })
126 if err != nil {
127 return internal.ErrNotSupported
128 }
129 _ = m.Close()
130 return nil
131 })
132
133 func wrapMapError(err error) error {
134 if err == nil {
135 return nil
136 }
137
138 if errors.Is(err, unix.ENOENT) {
139 return sys.Error(ErrKeyNotExist, unix.ENOENT)
140 }
141
142 if errors.Is(err, unix.EEXIST) {
143 return sys.Error(ErrKeyExist, unix.EEXIST)
144 }
145
146 if errors.Is(err, unix.ENOTSUPP) {
147 return sys.Error(ErrNotSupported, unix.ENOTSUPP)
148 }
149
150 if errors.Is(err, unix.E2BIG) {
151 return fmt.Errorf("key too big for map: %w", err)
152 }
153
154 return err
155 }
156
157 var haveObjName = internal.FeatureTest("object names", "4.15", func() error {
158 attr := sys.MapCreateAttr{
159 MapType: sys.MapType(Array),
160 KeySize: 4,
161 ValueSize: 4,
162 MaxEntries: 1,
163 MapName: sys.NewObjName("feature_test"),
164 }
165
166 fd, err := sys.MapCreate(&attr)
167 if err != nil {
168 return internal.ErrNotSupported
169 }
170
171 _ = fd.Close()
172 return nil
173 })
174
175 var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() error {
176 if err := haveObjName(); err != nil {
177 return err
178 }
179
180 attr := sys.MapCreateAttr{
181 MapType: sys.MapType(Array),
182 KeySize: 4,
183 ValueSize: 4,
184 MaxEntries: 1,
185 MapName: sys.NewObjName(".test"),
186 }
187
188 fd, err := sys.MapCreate(&attr)
189 if err != nil {
190 return internal.ErrNotSupported
191 }
192
193 _ = fd.Close()
194 return nil
195 })
196
197 var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error {
198 var maxEntries uint32 = 2
199 attr := sys.MapCreateAttr{
200 MapType: sys.MapType(Hash),
201 KeySize: 4,
202 ValueSize: 4,
203 MaxEntries: maxEntries,
204 }
205
206 fd, err := sys.MapCreate(&attr)
207 if err != nil {
208 return internal.ErrNotSupported
209 }
210 defer fd.Close()
211
212 keys := []uint32{1, 2}
213 values := []uint32{3, 4}
214 kp, _ := marshalPtr(keys, 8)
215 vp, _ := marshalPtr(values, 8)
216
217 err = sys.MapUpdateBatch(&sys.MapUpdateBatchAttr{
218 MapFd: fd.Uint(),
219 Keys: kp,
220 Values: vp,
221 Count: maxEntries,
222 })
223 if err != nil {
224 return internal.ErrNotSupported
225 }
226 return nil
227 })
228
229 var haveProbeReadKernel = internal.FeatureTest("bpf_probe_read_kernel", "5.5", func() error {
230 insns := asm.Instructions{
231 asm.Mov.Reg(asm.R1, asm.R10),
232 asm.Add.Imm(asm.R1, -8),
233 asm.Mov.Imm(asm.R2, 8),
234 asm.Mov.Imm(asm.R3, 0),
235 asm.FnProbeReadKernel.Call(),
236 asm.Return(),
237 }
238
239 fd, err := progLoad(insns, Kprobe, "GPL")
240 if err != nil {
241 return internal.ErrNotSupported
242 }
243 _ = fd.Close()
244 return nil
245 })
246
247 var haveBPFToBPFCalls = internal.FeatureTest("bpf2bpf calls", "4.16", func() error {
248 insns := asm.Instructions{
249 asm.Call.Label("prog2").WithSymbol("prog1"),
250 asm.Return(),
251 asm.Mov.Imm(asm.R0, 0).WithSymbol("prog2"),
252 asm.Return(),
253 }
254
255 fd, err := progLoad(insns, SocketFilter, "MIT")
256 if errors.Is(err, unix.EINVAL) {
257 return internal.ErrNotSupported
258 }
259 if err != nil {
260 return err
261 }
262 _ = fd.Close()
263 return nil
264 })
265
View as plain text