1 package ebpf
2
3 import (
4 "bytes"
5 "encoding"
6 "encoding/binary"
7 "errors"
8 "fmt"
9 "reflect"
10 "runtime"
11 "sync"
12 "unsafe"
13
14 "github.com/cilium/ebpf/internal"
15 "github.com/cilium/ebpf/internal/sys"
16 )
17
18
19
20
21
22
23 func marshalPtr(data interface{}, length int) (sys.Pointer, error) {
24 if ptr, ok := data.(unsafe.Pointer); ok {
25 return sys.NewPointer(ptr), nil
26 }
27
28 buf, err := marshalBytes(data, length)
29 if err != nil {
30 return sys.Pointer{}, err
31 }
32
33 return sys.NewSlicePointer(buf), nil
34 }
35
36
37
38
39
40
41
42
43 func marshalBytes(data interface{}, length int) (buf []byte, err error) {
44 if data == nil {
45 return nil, errors.New("can't marshal a nil value")
46 }
47
48 switch value := data.(type) {
49 case encoding.BinaryMarshaler:
50 buf, err = value.MarshalBinary()
51 case string:
52 buf = []byte(value)
53 case []byte:
54 buf = value
55 case unsafe.Pointer:
56 err = errors.New("can't marshal from unsafe.Pointer")
57 case Map, *Map, Program, *Program:
58 err = fmt.Errorf("can't marshal %T", value)
59 default:
60 var wr bytes.Buffer
61 err = binary.Write(&wr, internal.NativeEndian, value)
62 if err != nil {
63 err = fmt.Errorf("encoding %T: %v", value, err)
64 }
65 buf = wr.Bytes()
66 }
67 if err != nil {
68 return nil, err
69 }
70
71 if len(buf) != length {
72 return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length)
73 }
74 return buf, nil
75 }
76
77 func makeBuffer(dst interface{}, length int) (sys.Pointer, []byte) {
78 if ptr, ok := dst.(unsafe.Pointer); ok {
79 return sys.NewPointer(ptr), nil
80 }
81
82 buf := make([]byte, length)
83 return sys.NewSlicePointer(buf), buf
84 }
85
86 var bytesReaderPool = sync.Pool{
87 New: func() interface{} {
88 return new(bytes.Reader)
89 },
90 }
91
92
93
94
95
96
97
98
99 func unmarshalBytes(data interface{}, buf []byte) error {
100 switch value := data.(type) {
101 case unsafe.Pointer:
102 dst := unsafe.Slice((*byte)(value), len(buf))
103 copy(dst, buf)
104 runtime.KeepAlive(value)
105 return nil
106 case Map, *Map, Program, *Program:
107 return fmt.Errorf("can't unmarshal into %T", value)
108 case encoding.BinaryUnmarshaler:
109 return value.UnmarshalBinary(buf)
110 case *string:
111 *value = string(buf)
112 return nil
113 case *[]byte:
114 *value = buf
115 return nil
116 case *int32:
117 if len(buf) < 4 {
118 return errors.New("int32 requires 4 bytes")
119 }
120 *value = int32(internal.NativeEndian.Uint32(buf))
121 return nil
122 case *uint32:
123 if len(buf) < 4 {
124 return errors.New("uint32 requires 4 bytes")
125 }
126 *value = internal.NativeEndian.Uint32(buf)
127 return nil
128 case *int64:
129 if len(buf) < 8 {
130 return errors.New("int64 requires 8 bytes")
131 }
132 *value = int64(internal.NativeEndian.Uint64(buf))
133 return nil
134 case *uint64:
135 if len(buf) < 8 {
136 return errors.New("uint64 requires 8 bytes")
137 }
138 *value = internal.NativeEndian.Uint64(buf)
139 return nil
140 case string:
141 return errors.New("require pointer to string")
142 case []byte:
143 return errors.New("require pointer to []byte")
144 default:
145 rd := bytesReaderPool.Get().(*bytes.Reader)
146 rd.Reset(buf)
147 defer bytesReaderPool.Put(rd)
148 if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
149 return fmt.Errorf("decoding %T: %v", value, err)
150 }
151 return nil
152 }
153 }
154
155
156
157
158
159
160
161 func marshalPerCPUValue(slice interface{}, elemLength int) (sys.Pointer, error) {
162 sliceType := reflect.TypeOf(slice)
163 if sliceType.Kind() != reflect.Slice {
164 return sys.Pointer{}, errors.New("per-CPU value requires slice")
165 }
166
167 possibleCPUs, err := internal.PossibleCPUs()
168 if err != nil {
169 return sys.Pointer{}, err
170 }
171
172 sliceValue := reflect.ValueOf(slice)
173 sliceLen := sliceValue.Len()
174 if sliceLen > possibleCPUs {
175 return sys.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
176 }
177
178 alignedElemLength := internal.Align(elemLength, 8)
179 buf := make([]byte, alignedElemLength*possibleCPUs)
180
181 for i := 0; i < sliceLen; i++ {
182 elem := sliceValue.Index(i).Interface()
183 elemBytes, err := marshalBytes(elem, elemLength)
184 if err != nil {
185 return sys.Pointer{}, err
186 }
187
188 offset := i * alignedElemLength
189 copy(buf[offset:offset+elemLength], elemBytes)
190 }
191
192 return sys.NewSlicePointer(buf), nil
193 }
194
195
196
197
198
199 func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
200 slicePtrType := reflect.TypeOf(slicePtr)
201 if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
202 return fmt.Errorf("per-cpu value requires pointer to slice")
203 }
204
205 possibleCPUs, err := internal.PossibleCPUs()
206 if err != nil {
207 return err
208 }
209
210 sliceType := slicePtrType.Elem()
211 slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)
212
213 sliceElemType := sliceType.Elem()
214 sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
215 if sliceElemIsPointer {
216 sliceElemType = sliceElemType.Elem()
217 }
218
219 step := len(buf) / possibleCPUs
220 if step < elemLength {
221 return fmt.Errorf("per-cpu element length is larger than available data")
222 }
223 for i := 0; i < possibleCPUs; i++ {
224 var elem interface{}
225 if sliceElemIsPointer {
226 newElem := reflect.New(sliceElemType)
227 slice.Index(i).Set(newElem)
228 elem = newElem.Interface()
229 } else {
230 elem = slice.Index(i).Addr().Interface()
231 }
232
233
234 elemBytes := make([]byte, elemLength)
235 copy(elemBytes, buf[:elemLength])
236
237 err := unmarshalBytes(elem, elemBytes)
238 if err != nil {
239 return fmt.Errorf("cpu %d: %w", i, err)
240 }
241
242 buf = buf[step:]
243 }
244
245 reflect.ValueOf(slicePtr).Elem().Set(slice)
246 return nil
247 }
248
View as plain text