1 package features
2
3 import (
4 "bytes"
5 "errors"
6 "fmt"
7 "sync"
8
9 "github.com/cilium/ebpf"
10 "github.com/cilium/ebpf/asm"
11 "github.com/cilium/ebpf/internal"
12 "github.com/cilium/ebpf/internal/sys"
13 "github.com/cilium/ebpf/internal/unix"
14 )
15
16 func init() {
17 miscs.miscTypes = make(map[miscType]error)
18 }
19
20 var (
21 miscs miscCache
22 )
23
24 type miscCache struct {
25 sync.Mutex
26 miscTypes map[miscType]error
27 }
28
29 type miscType uint32
30
31 const (
32
33
34 largeInsn miscType = iota
35
36
37 boundedLoops
38
39
40 v2ISA
41
42
43 v3ISA
44 )
45
46 const (
47 maxInsns = 4096
48 )
49
50
51
52
53
54 func HaveLargeInstructions() error {
55 return probeMisc(largeInsn)
56 }
57
58
59
60
61 func HaveBoundedLoops() error {
62 return probeMisc(boundedLoops)
63 }
64
65
66
67
68 func HaveV2ISA() error {
69 return probeMisc(v2ISA)
70 }
71
72
73
74
75 func HaveV3ISA() error {
76 return probeMisc(v3ISA)
77 }
78
79
80
81 func probeMisc(mt miscType) error {
82 mc.Lock()
83 defer mc.Unlock()
84 err, ok := miscs.miscTypes[mt]
85 if ok {
86 return err
87 }
88
89 attr, err := createMiscProbeAttr(mt)
90 if err != nil {
91 return fmt.Errorf("couldn't create the attributes for the probe: %w", err)
92 }
93
94 fd, err := sys.ProgLoad(attr)
95
96 switch {
97
98
99
100
101 case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
102 err = ebpf.ErrNotSupported
103
104
105 case errors.Is(err, unix.EPERM):
106 break
107
108
109 case err != nil:
110 err = fmt.Errorf("unexpected error during feature probe: %w", err)
111
112 default:
113 fd.Close()
114 }
115
116 miscs.miscTypes[mt] = err
117
118 return err
119 }
120
121 func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) {
122 var insns asm.Instructions
123 switch mt {
124 case largeInsn:
125 for i := 0; i < maxInsns; i++ {
126 insns = append(insns, asm.Mov.Imm(asm.R0, 1))
127 }
128 insns = append(insns, asm.Return())
129 case boundedLoops:
130 insns = asm.Instructions{
131 asm.Mov.Imm(asm.R0, 10),
132 asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"),
133 asm.JNE.Imm(asm.R0, 0, "loop"),
134 asm.Return(),
135 }
136 case v2ISA:
137 insns = asm.Instructions{
138 asm.Mov.Imm(asm.R0, 0),
139 asm.JLT.Imm(asm.R0, 0, "exit"),
140 asm.Mov.Imm(asm.R0, 1),
141 asm.Return().WithSymbol("exit"),
142 }
143 case v3ISA:
144 insns = asm.Instructions{
145 asm.Mov.Imm(asm.R0, 0),
146 asm.JLT.Imm32(asm.R0, 0, "exit"),
147 asm.Mov.Imm(asm.R0, 1),
148 asm.Return().WithSymbol("exit"),
149 }
150 default:
151 return nil, fmt.Errorf("misc probe %d not implemented", mt)
152 }
153
154 buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
155 if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
156 return nil, err
157 }
158
159 bytecode := buf.Bytes()
160 instructions := sys.NewSlicePointer(bytecode)
161
162 return &sys.ProgLoadAttr{
163 ProgType: sys.BPF_PROG_TYPE_SOCKET_FILTER,
164 Insns: instructions,
165 InsnCnt: uint32(len(bytecode) / asm.InstructionSize),
166 License: sys.NewStringPointer("MIT"),
167 }, nil
168 }
169
View as plain text