Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new: Support for new field types in plugins #71

Merged
merged 6 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 86 additions & 4 deletions pkg/sdk/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ package sdk
typedef union {
const char* str;
uint64_t u64;
uint32_t u32;
bool boolean;
ss_plugin_byte_buffer buf;
} field_result_t;

*/
import "C"
import (
"unsafe"
"reflect"

"github.com/falcosecurity/plugin-sdk-go/pkg/ptr"
)
Expand All @@ -44,6 +48,11 @@ const (
minResultBufferLen = 512
)

type ConstSizedBuffer = struct {
Size uint32
Buf []byte
}

// ExtractRequest represents an high-level abstraction that wraps a pointer to
// a ss_plugin_extract_field C structure, providing methods for accessing its
// fields in a go-friendly way.
Expand All @@ -53,8 +62,16 @@ type ExtractRequest interface {
FieldID() uint64
//
// FieldType returns the type of the field for which the value extraction
// is requested. For now, only sdk.FieldTypeUint64 and
// sdk.FieldTypeCharBuf are supported.
// is requested. For now, the supported types are:
// - sdk.FieldTypeUint64
// - sdk.FieldTypeCharBuf
// - sdk.FieldTypeIPv4Addr
// - sdk.FieldTypeRelTime
// - sdk.FieldTypeAbsTime
// - sdk.FieldTypeBool
// - sdk.FieldTypeIPv4Net
// - sdk.FieldTypeIPv6Addr
// - sdk.FieldTypeIPv6Net
FieldType() uint32
//
// Field returns the name of the field for which the value extraction
Expand Down Expand Up @@ -143,6 +160,8 @@ type extractRequest struct {
resBufLen uint32
// List of StringBuffer to return string results
resStrBufs []StringBuffer
// List of BytesReadWriter to return binary results
resBinBufs []ptr.BytesReadWriter
}

func (e *extractRequest) SetPtr(pef unsafe.Pointer) {
Expand Down Expand Up @@ -179,7 +198,7 @@ func (e *extractRequest) IsList() bool {

func (e *extractRequest) SetValue(v interface{}) {
switch e.FieldType() {
case FieldTypeUint64:
case FieldTypeRelTime, FieldTypeAbsTime, FieldTypeUint64:
if e.req.flist {
if e.resBufLen < uint32(len(v.([]uint64))) {
C.free(unsafe.Pointer(e.resBuf))
Expand All @@ -194,12 +213,42 @@ func (e *extractRequest) SetValue(v interface{}) {
*((*C.uint64_t)(unsafe.Pointer(e.resBuf))) = (C.uint64_t)(v.(uint64))
e.req.res_len = (C.uint64_t)(1)
}

case FieldTypeIPv4Net, FieldTypeIPv6Addr, FieldTypeIPv6Net:
if e.req.flist {
if e.resBufLen < uint32(len(v.([]ConstSizedBuffer))) {
C.free(unsafe.Pointer(e.resBuf))
e.resBufLen = uint32(len(v.([]ConstSizedBuffer)))
e.resBuf = (*C.field_result_t)(C.malloc((C.size_t)((e.resBufLen) * C.sizeof_field_result_t)))
}
for i, goBuf := range v.([]ConstSizedBuffer) {
cBuf := C.struct_ss_plugin_byte_buffer{
len: C.uint32_t(goBuf.Size),
ptr: unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&goBuf.Buf)).Data),
}

*((*C.struct_ss_plugin_byte_buffer)(unsafe.Pointer(uintptr(unsafe.Pointer(e.resBuf)) + uintptr(i*C.sizeof_field_result_t)))) = (C.struct_ss_plugin_byte_buffer)(cBuf)
}
e.req.res_len = (C.uint64_t)(len(v.([]ConstSizedBuffer)))
} else {
goBuf := v.(ConstSizedBuffer)

cBuf := C.struct_ss_plugin_byte_buffer{
len: C.uint32_t(goBuf.Size),
ptr: unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&goBuf.Buf)).Data),
}

*((*C.struct_ss_plugin_byte_buffer)(unsafe.Pointer(e.resBuf))) = (C.struct_ss_plugin_byte_buffer)(cBuf)

e.req.res_len = (C.uint64_t)(1)
}

case FieldTypeCharBuf:
if e.req.flist {
if e.resBufLen < uint32(len(v.([]string))) {
C.free(unsafe.Pointer(e.resBuf))
e.resBufLen = uint32(len(v.([]string)))
e.resBuf = (*C.field_result_t)(C.malloc((C.size_t)(e.resBufLen * C.sizeof_field_result_t)))
e.resBuf = (*C.field_result_t)(C.malloc((C.size_t)((e.resBufLen) * C.sizeof_field_result_t)))
}
for i, val := range v.([]string) {
if len(e.resStrBufs) <= i {
Expand All @@ -214,6 +263,39 @@ func (e *extractRequest) SetValue(v interface{}) {
*((**C.char)(unsafe.Pointer(e.resBuf))) = (*C.char)(e.resStrBufs[0].CharPtr())
e.req.res_len = (C.uint64_t)(1)
}

case FieldTypeBool:
if e.req.flist {
if e.resBufLen < uint32(len(v.([]bool))) {
C.free(unsafe.Pointer(e.resBuf))
e.resBufLen = uint32(len(v.([]bool)))
e.resBuf = (*C.field_result_t)(C.malloc((C.size_t)(e.resBufLen * C.sizeof_field_result_t)))
}
for i, val := range v.([]bool) {
*((*C.bool)(unsafe.Pointer(uintptr(unsafe.Pointer(e.resBuf)) + uintptr(i*C.sizeof_field_result_t)))) = (C.bool)(val)
}
e.req.res_len = (C.uint64_t)(len(v.([]bool)))
} else {
*((*C.bool)(unsafe.Pointer(e.resBuf))) = (C.bool)(v.(bool))
e.req.res_len = (C.uint64_t)(1)
}

case FieldTypeIPv4Addr:
if e.req.flist {
if e.resBufLen < uint32(len(v.([]uint32))) {
C.free(unsafe.Pointer(e.resBuf))
e.resBufLen = uint32(len(v.([]uint32)))
e.resBuf = (*C.field_result_t)(C.malloc((C.size_t)(e.resBufLen * C.sizeof_field_result_t)))
}
for i, val := range v.([]uint32) {
*((*C.uint32_t)(unsafe.Pointer(uintptr(unsafe.Pointer(e.resBuf)) + uintptr(i*C.sizeof_field_result_t)))) = (C.uint32_t)(val)
}
e.req.res_len = (C.uint64_t)(len(v.([]uint32)))
} else {
*((*C.uint32_t)(unsafe.Pointer(e.resBuf))) = (C.uint32_t)(v.(uint32))
e.req.res_len = (C.uint64_t)(1)
}

default:
panic("plugin-sdk-go/sdk: called SetValue with unsupported field type")
}
Expand Down
110 changes: 109 additions & 1 deletion pkg/sdk/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ func allocSSPluginExtractField(fid, ftype uint32, fname, farg_key string, farg_i
}
}

func getBoolResSSPluingExtractField(t *testing.T, ptr *_Ctype_ss_plugin_extract_field, index int) bool {
if ptr.res_len < (_Ctype_uint64_t)(index) {
t.Errorf("trying to access extract field res at index %d, but res len is %d", index, (int)(ptr.res_len))
}
value := (uint8)(*((*_Ctype_uint32_t)(unsafe.Pointer(uintptr(*(*_Ctype_uintptr_t)(unsafe.Pointer(&ptr.res))) + uintptr(index*_Ciconst_sizeof_field_result_t)))))
return value != uint8(0)
}

func getStrResSSPluingExtractField(t *testing.T, p *_Ctype_ss_plugin_extract_field, index int) string {
if p.res_len < (_Ctype_uint64_t)(index) {
t.Errorf("trying to access extract field res at index %d, but res len is %d", index, (int)(p.res_len))
Expand All @@ -63,6 +71,26 @@ func getU64ResSSPluingExtractField(t *testing.T, ptr *_Ctype_ss_plugin_extract_f
return (uint64)(*((*_Ctype_uint64_t)(unsafe.Pointer(uintptr(*(*_Ctype_uintptr_t)(unsafe.Pointer(&ptr.res))) + uintptr(index*_Ciconst_sizeof_field_result_t)))))
}

func getBinResSSPluingExtractField(t *testing.T, p *_Ctype_ss_plugin_extract_field, index int) ConstSizedBuffer {
if p.res_len < (_Ctype_uint64_t)(index) {
t.Errorf("trying to access extract field res at index %d, but res len is %d", index, (int)(p.res_len))
}

bufListPtr := *(*unsafe.Pointer)(unsafe.Pointer(&p.res))
curBufPtr := (unsafe.Pointer)(unsafe.Pointer(uintptr(bufListPtr) + uintptr(index*_Ciconst_sizeof_field_result_t)))
size := *(*uint32)(curBufPtr)
buf := make([]byte, size)
ptrBytes := *(*unsafe.Pointer)(unsafe.Pointer(uintptr(curBufPtr) + uintptr(8)))
for i:=0 ; i < int(size); i++{
buf[i] = *(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(ptrBytes))+uintptr(i)))
}

return ConstSizedBuffer{
Size: size,
Buf: buf,
}
}

func assertPanic(t *testing.T, fun func()) {
defer func() {
if r := recover(); r == nil {
Expand Down Expand Up @@ -93,11 +121,26 @@ func TestExtractRequestSetValue(t *testing.T) {
// init test data
testStr := "test str"
testU64 := uint64(99)
testBool := true
testData := []byte{ 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80 }
testIPv6 := ConstSizedBuffer{
Size: uint32(len(testData)),
Buf: testData,
}
testStrList := make([]string, 0)
testU64List := make([]uint64, 0)
for i := 0; i < minResultBufferLen+1; i++ { // cause a list resizing
testBoolList := make([]bool, 0)
dataArray := make([]byte, (minResultBufferLen+1)*int(testIPv6.Size))
for i := 0; i < (minResultBufferLen+1)*int(testIPv6.Size); i++ {
dataArray[i] = byte(i)
}
testIPv6List := make([]ConstSizedBuffer, minResultBufferLen+1)
for i := 0; i < minResultBufferLen+1; i++ {
testStrList = append(testStrList, fmt.Sprintf("test-%d", i))
testU64List = append(testU64List, uint64(i))
testBoolList = append(testBoolList, i%3==0)
testIPv6List[i].Buf = dataArray[i*int(testIPv6.Size) : (i+1)*int(testIPv6.Size)]
testIPv6List[i].Size = testIPv6.Size
}

// init extract requests
Expand All @@ -106,14 +149,26 @@ func TestExtractRequestSetValue(t *testing.T) {
u64ListPtr, freeU64ListPtr := allocSSPluginExtractField(2, FieldTypeUint64, "test.u64", "", 0, true, true)
strPtr, freeStrPtr := allocSSPluginExtractField(3, FieldTypeCharBuf, "test.str", "", 0, true, false)
strListPtr, freeStrListPtr := allocSSPluginExtractField(4, FieldTypeCharBuf, "test.str", "", 0, true, true)
boolPtr, freeBoolPtr := allocSSPluginExtractField(5, FieldTypeBool, "test.bool", "", 0, true, false)
boolListPtr, freeBoolListPtr := allocSSPluginExtractField(6, FieldTypeBool, "test.bool", "", 0, true, true)
binPtr, freeBinPtr := allocSSPluginExtractField(7, FieldTypeIPv6Addr, "test.ipv6addr", "", 0, true, false)
binListPtr, freeBinListPtr := allocSSPluginExtractField(8, FieldTypeIPv6Addr, "test.ipv6addr", "", 0, true, true)
u64Req := pool.Get(0)
u64ReqList := pool.Get(1)
strReq := pool.Get(2)
strReqList := pool.Get(3)
boolReq := pool.Get(4)
boolReqList := pool.Get(5)
binReq := pool.Get(6)
binReqList := pool.Get(7)
u64Req.SetPtr(unsafe.Pointer(u64Ptr))
u64ReqList.SetPtr(unsafe.Pointer(u64ListPtr))
strReq.SetPtr(unsafe.Pointer(strPtr))
strReqList.SetPtr(unsafe.Pointer(strListPtr))
boolReq.SetPtr(unsafe.Pointer(boolPtr))
boolReqList.SetPtr(unsafe.Pointer(boolListPtr))
binReq.SetPtr(unsafe.Pointer(binPtr))
binReqList.SetPtr(unsafe.Pointer(binListPtr))

// check that info is passed-through correctly
if u64Req.FieldID() != 1 {
Expand Down Expand Up @@ -144,9 +199,23 @@ func TestExtractRequestSetValue(t *testing.T) {
// check panics
assertPanic(t, func() {
u64Req.SetValue("test")
u64Req.SetValue(bool(true))
boolReq.SetValue([]byte{ 0x41, 0x41, 0x41, 0x41})
})
assertPanic(t, func() {
strReq.SetValue(uint64(1))
strReq.SetValue(bool(true))
boolReq.SetValue([]byte{ 0x41, 0x41, 0x41, 0x41})
})
assertPanic(t, func() {
boolReq.SetValue(uint64(1))
boolReq.SetValue("test")
boolReq.SetValue([]byte{ 0x41, 0x41, 0x41, 0x41})
})
assertPanic(t, func() {
binReq.SetValue(uint64(1))
binReq.SetValue("test")
binReq.SetValue(bool(true))
})

// check set correct values
Expand All @@ -170,10 +239,49 @@ func TestExtractRequestSetValue(t *testing.T) {
t.Errorf("expected value '%s', but found '%s'", s, getStrResSSPluingExtractField(t, strPtr, i))
}
}
boolReq.SetValue(testBool)
if getBoolResSSPluingExtractField(t, boolPtr, 0) != testBool {
t.Errorf("expected value '%v', but found '%v'", testBool, getBoolResSSPluingExtractField(t, boolPtr, 0))
}
boolReqList.SetValue(testBoolList)
for i, b := range testBoolList {
if getBoolResSSPluingExtractField(t, boolListPtr, i) != b {
t.Errorf("expected value '%v', but found '%v' at index %d", b, getBoolResSSPluingExtractField(t, boolPtr, i),i)
}
}
binReq.SetValue(testIPv6)
testIPv6res := getBinResSSPluingExtractField(t, binPtr, 0)
if testIPv6res.Size != testIPv6.Size {
t.Errorf("expected value '%v', but found '%v'", testIPv6, getBinResSSPluingExtractField(t, binPtr, 0))
} else {
for i:=0 ; uint32(i)<testIPv6.Size ; i++ {
if testIPv6.Buf[i] != testIPv6res.Buf[i] {
t.Errorf("expected value '%v', but found '%v'", testIPv6, getBinResSSPluingExtractField(t, binPtr, 0))
}
}
}
binReqList.SetValue(testIPv6List)
for i, s := range testIPv6List {
testIPv6res := getBinResSSPluingExtractField(t, binListPtr, i)
if testIPv6res.Size != s.Size {
t.Errorf("expected size '%v', but found '%v'", s.Size, testIPv6res.Size)
} else {
for k:=0 ; uint32(k)<s.Size ; k++ {
if s.Buf[k] != testIPv6res.Buf[k] {
t.Errorf("expected value '%v', but found '%v'", s.Buf, testIPv6res.Buf)
}
}
}

}

pool.Free()
freeU64Ptr()
freeU64ListPtr()
freeStrPtr()
freeStrListPtr()
freeBoolPtr()
freeBoolListPtr()
freeBinPtr()
freeBinListPtr()
}
4 changes: 2 additions & 2 deletions pkg/sdk/plugin_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extern "C" {
// API versions of this plugin framework
//
#define PLUGIN_API_VERSION_MAJOR 2
#define PLUGIN_API_VERSION_MINOR 0
#define PLUGIN_API_VERSION_MINOR 1
#define PLUGIN_API_VERSION_PATCH 0

//
Expand Down Expand Up @@ -387,4 +387,4 @@ typedef struct

#ifdef __cplusplus
}
#endif
#endif
24 changes: 20 additions & 4 deletions pkg/sdk/plugin_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ extern "C" {

#include <stdbool.h>
#include <inttypes.h>
#include <stddef.h>

// The noncontinguous numbers are to maintain equality with underlying
// falcosecurity libs types.
// falcosecurity libs types (ppm_events_public.h).
typedef enum ss_plugin_field_type
{
FTYPE_UINT64 = 8,
FTYPE_STRING = 9
FTYPE_UINT64 = 8,
FTYPE_STRING = 9,
FTYPE_IPV4ADDR = 12,
FTYPE_RELTIME = 20,
FTYPE_ABSTIME = 21,
FTYPE_BOOL = 25,
FTYPE_IPV4NET = 37,
FTYPE_IPV6ADDR = 38,
FTYPE_IPV6NET = 39,
} ss_plugin_field_type;

// Values to return from init() / open() / next_batch() /
Expand Down Expand Up @@ -78,6 +86,11 @@ typedef struct ss_plugin_event
uint64_t ts;
} ss_plugin_event;

typedef struct ss_plugin_byte_buffer {
uint32_t len;
const void* ptr;
} ss_plugin_byte_buffer;

// Used in extract_fields functions below to receive a field/arg
// pair and return an extracted value.
// field_id: id of the field, as of its index in the list of
Expand Down Expand Up @@ -126,6 +139,9 @@ typedef struct ss_plugin_extract_field
{
const char** str;
uint64_t* u64;
uint32_t* u32;
bool* boolean;
ss_plugin_byte_buffer* buf;
} res;
uint64_t res_len;

Expand Down Expand Up @@ -162,4 +178,4 @@ typedef void ss_instance_t;

#ifdef __cplusplus
}
#endif
#endif
Loading