-
Notifications
You must be signed in to change notification settings - Fork 28
/
ffi.go
162 lines (138 loc) · 4.72 KB
/
ffi.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
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package units
//#include <linux/bpf.h>
//#include <stdint.h>
//#include <stdlib.h>
//struct bpf_result {
// char* serialized_proto;
// size_t size;
//};
//struct bpf_result ffi_load_bpf_program(void* prog_buff, size_t size, int coverage_enabled, unsigned long coverage_size);
//struct bpf_result ffi_execute_bpf_program(void* serialized_proto, size_t length);
//struct bpf_result ffi_get_map_elements(int map_fd, uint64_t map_size);
//int ffi_create_bpf_map(size_t size);
//void ffi_close_fd(int fd);
//int ffi_update_map_element(int map_fd, int key, uint64_t value);
import "C"
import (
"encoding/base64"
"fmt"
"unsafe"
fpb "buzzer/proto/ffi_go_proto"
"github.com/golang/protobuf/proto"
)
// Takes the results returned by the c FFI and reconstructs the result proto.
// This will release the memory allocated by the c ffi and set the pointer in
// the struct to null so it doesn't get reused.
func protoDataFromStruct(s *C.struct_bpf_result) ([]byte, error) {
if s.serialized_proto == nil {
return nil, fmt.Errorf("serialized proto is nil")
}
defer func() {
C.free(unsafe.Pointer(s.serialized_proto))
s.serialized_proto = nil
}()
pb64 := C.GoStringN(s.serialized_proto, C.int(s.size))
data, err := base64.StdEncoding.DecodeString(pb64)
if err != nil {
return nil, err
}
return data, nil
}
func validationProtoFromStruct(s *C.struct_bpf_result) (*fpb.ValidationResult, error) {
data, err := protoDataFromStruct(s)
if err != nil {
return nil, err
}
res := &fpb.ValidationResult{}
if err := proto.Unmarshal(data, res); err != nil {
return nil, err
}
return res, nil
}
func executionProtoFromStruct(s *C.struct_bpf_result) (*fpb.ExecutionResult, error) {
data, err := protoDataFromStruct(s)
if err != nil {
return nil, err
}
res := &fpb.ExecutionResult{}
if err := proto.Unmarshal(data, res); err != nil {
return nil, err
}
return res, nil
}
func mapElementsProtoFromStruct(s *C.struct_bpf_result) (*fpb.MapElements, error) {
data, err := protoDataFromStruct(s)
if err != nil {
return nil, err
}
res := &fpb.MapElements{}
if err := proto.Unmarshal(data, res); err != nil {
return nil, err
}
return res, nil
}
// FFI is the unit that will talk to ebpf and run/validate programs.
type FFI struct {
MetricsUnit *Metrics
}
// ValidateProgram passes the program through the bpf verifier without executing
// it. Returns feedback to the generator so it can adjust the generation
// settings.
func (e *FFI) ValidateProgram(prog []uint64) (*fpb.ValidationResult, error) {
if len(prog) == 0 {
return nil, fmt.Errorf("cannot run empty program")
}
shouldCollect, coverageSize := e.MetricsUnit.ShouldGetCoverage()
cbool := 0
if shouldCollect {
cbool = 1
}
bpfVerifyResult := C.ffi_load_bpf_program(unsafe.Pointer(&prog[0]), C.ulong(len(prog)) /*enable_coverage=*/, C.int(cbool) /*coverage_size=*/, C.ulong(coverageSize))
res, err := validationProtoFromStruct(&bpfVerifyResult)
if err != nil {
return nil, err
}
e.MetricsUnit.RecordVerificationResults(res)
return res, nil
}
// RunProgram Runs the ebpf program and returns the execution results.
func (e *FFI) RunProgram(executionRequest *fpb.ExecutionRequest) (*fpb.ExecutionResult, error) {
serializedProto, err := proto.Marshal(executionRequest)
if err != nil {
return nil, err
}
res := C.ffi_execute_bpf_program(unsafe.Pointer(&serializedProto[0]), C.ulong(len(serializedProto)))
return executionProtoFromStruct(&res)
}
// CreateMapArray creates an ebpf map of type array and returns its fd.
// -1 means error.
func (e *FFI) CreateMapArray(size uint64) int {
return int(C.ffi_create_bpf_map(C.ulong(size)))
}
// CloseFD closes the provided file descriptor.
func (e *FFI) CloseFD(fd int) {
C.ffi_close_fd(C.int(fd))
}
// GetMapElements fetches the map elements of the given fd.
func (e *FFI) GetMapElements(fd int, mapSize uint64) (*fpb.MapElements, error) {
res := C.ffi_get_map_elements(C.int(fd), C.ulong(mapSize))
return mapElementsProtoFromStruct(&res)
}
// SetMapElement sets the elemnt specified by `key` to `value` in the map
// described by `fd`
func (e *FFI) SetMapElement(fd int, key uint32, value uint64) int {
return int(C.ffi_update_map_element(C.int(fd), C.int(key), C.ulong(value)))
}