Skip to content

Commit ae22611

Browse files
committed
ringbuf: Windows support
Signed-off-by: Lorenz Bauer <[email protected]>
1 parent c9de606 commit ae22611

File tree

11 files changed

+578
-189
lines changed

11 files changed

+578
-189
lines changed

internal/efw/map.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//go:build windows
2+
3+
package efw
4+
5+
import (
6+
"runtime"
7+
"syscall"
8+
"unsafe"
9+
10+
"golang.org/x/sys/windows"
11+
)
12+
13+
/*
14+
ebpf_ring_buffer_map_map_buffer(
15+
16+
fd_t map_fd,
17+
_Outptr_result_maybenull_ void** consumer,
18+
_Outptr_result_maybenull_ const void** producer,
19+
_Outptr_result_buffer_maybenull_(*data_size) const uint8_t** data,
20+
_Out_ size_t* data_size) EBPF_NO_EXCEPT;
21+
*/
22+
var ebpfRingBufferMapMapBufferProc = newProc("ebpf_ring_buffer_map_map_buffer")
23+
24+
func EbpfRingBufferMapMapBuffer(mapFd int) (consumer, producer, data *uint8, dataLen Size, _ error) {
25+
addr, err := ebpfRingBufferMapMapBufferProc.Find()
26+
if err != nil {
27+
return nil, nil, nil, 0, err
28+
}
29+
30+
err = errorResult(syscall.SyscallN(addr,
31+
uintptr(mapFd),
32+
uintptr(unsafe.Pointer(&consumer)),
33+
uintptr(unsafe.Pointer(&producer)),
34+
uintptr(unsafe.Pointer(&data)),
35+
uintptr(unsafe.Pointer(&dataLen)),
36+
))
37+
if err != nil {
38+
return nil, nil, nil, 0, err
39+
}
40+
41+
return consumer, producer, data, dataLen, nil
42+
}
43+
44+
/*
45+
ebpf_ring_buffer_map_unmap_buffer(
46+
47+
fd_t map_fd, _In_ void* consumer, _In_ const void* producer, _In_ const void* data) EBPF_NO_EXCEPT;
48+
*/
49+
var ebpfRingBufferMapUnmapBufferProc = newProc("ebpf_ring_buffer_map_unmap_buffer")
50+
51+
func EbpfRingBufferMapUnmapBuffer(mapFd int, consumer, producer, data *uint8) error {
52+
addr, err := ebpfRingBufferMapUnmapBufferProc.Find()
53+
if err != nil {
54+
return err
55+
}
56+
57+
return errorResult(syscall.SyscallN(addr,
58+
uintptr(mapFd),
59+
uintptr(unsafe.Pointer(consumer)),
60+
uintptr(unsafe.Pointer(producer)),
61+
uintptr(unsafe.Pointer(data)),
62+
))
63+
}
64+
65+
/*
66+
ebpf_result_t ebpf_map_set_wait_handle(
67+
68+
fd_t map_fd,
69+
uint64_t index,
70+
ebpf_handle_t handle)
71+
*/
72+
var ebpfMapSetWaitHandleProc = newProc("ebpf_map_set_wait_handle")
73+
74+
func EbpfMapSetWaitHandle(mapFd int, index uint64, handle windows.Handle) error {
75+
addr, err := ebpfMapSetWaitHandleProc.Find()
76+
if err != nil {
77+
return err
78+
}
79+
80+
return errorResult(syscall.SyscallN(addr,
81+
uintptr(mapFd),
82+
uintptr(index),
83+
uintptr(handle),
84+
))
85+
}
86+
87+
/*
88+
ebpf_result_t ebpf_ring_buffer_map_write(
89+
90+
fd_t ring_buffer_map_fd,
91+
const void* data,
92+
size_t data_length)
93+
*/
94+
var ebpfRingBufferMapWriteProc = newProc("ebpf_ring_buffer_map_write")
95+
96+
func EbpfRingBufferMapWrite(ringBufferMapFd int, data []byte) error {
97+
addr, err := ebpfRingBufferMapWriteProc.Find()
98+
if err != nil {
99+
return err
100+
}
101+
102+
err = errorResult(syscall.SyscallN(addr,
103+
uintptr(ringBufferMapFd),
104+
uintptr(unsafe.Pointer(&data[0])),
105+
uintptr(len(data)),
106+
))
107+
runtime.KeepAlive(data)
108+
return err
109+
}

ringbuf/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package ringbuf allows interacting with Linux BPF ring buffer.
1+
// Package ringbuf allows interacting with the BPF ring buffer.
22
//
33
// BPF allows submitting custom events to a BPF ring buffer map set up
44
// by userspace. This is very useful to push things like packet samples

ringbuf/helper_other_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//go:build !windows
2+
3+
package ringbuf
4+
5+
import (
6+
"testing"
7+
8+
"github.com/go-quicktest/qt"
9+
10+
"github.com/cilium/ebpf"
11+
"github.com/cilium/ebpf/asm"
12+
)
13+
14+
func mustOutputSamplesProg(tb testing.TB, sampleMessages ...sampleMessage) (*ebpf.Program, *ebpf.Map) {
15+
tb.Helper()
16+
17+
events, err := ebpf.NewMap(&ebpf.MapSpec{
18+
Type: ebpf.RingBuf,
19+
MaxEntries: 4096,
20+
})
21+
qt.Assert(tb, qt.IsNil(err))
22+
tb.Cleanup(func() {
23+
events.Close()
24+
})
25+
26+
var maxSampleSize int
27+
for _, sampleMessage := range sampleMessages {
28+
if sampleMessage.size > maxSampleSize {
29+
maxSampleSize = sampleMessage.size
30+
}
31+
}
32+
33+
insns := asm.Instructions{
34+
asm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord),
35+
asm.Mov.Reg(asm.R9, asm.R1),
36+
}
37+
38+
bufDwords := (maxSampleSize / 8) + 1
39+
for i := range bufDwords {
40+
insns = append(insns,
41+
asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord),
42+
)
43+
}
44+
45+
for _, sampleMessage := range sampleMessages {
46+
insns = append(insns,
47+
asm.LoadMapPtr(asm.R1, events.FD()),
48+
asm.Mov.Imm(asm.R2, int32(sampleMessage.size)),
49+
asm.Mov.Imm(asm.R3, int32(0)),
50+
asm.FnRingbufReserve.Call(),
51+
asm.JEq.Imm(asm.R0, 0, "exit"),
52+
asm.Mov.Reg(asm.R5, asm.R0),
53+
)
54+
for i := range sampleMessage.size {
55+
insns = append(insns,
56+
asm.LoadMem(asm.R4, asm.RFP, int16(i+1)*-1, asm.Byte),
57+
asm.StoreMem(asm.R5, int16(i), asm.R4, asm.Byte),
58+
)
59+
}
60+
61+
if sampleMessage.discard {
62+
insns = append(insns,
63+
asm.Mov.Reg(asm.R1, asm.R5),
64+
asm.Mov.Imm(asm.R2, sampleMessage.flags),
65+
asm.FnRingbufDiscard.Call(),
66+
)
67+
} else {
68+
insns = append(insns,
69+
asm.Mov.Reg(asm.R1, asm.R5),
70+
asm.Mov.Imm(asm.R2, sampleMessage.flags),
71+
asm.FnRingbufSubmit.Call(),
72+
)
73+
}
74+
}
75+
76+
insns = append(insns,
77+
asm.Mov.Imm(asm.R0, int32(0)).WithSymbol("exit"),
78+
asm.Return(),
79+
)
80+
81+
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
82+
License: "MIT",
83+
Type: ebpf.XDP,
84+
Instructions: insns,
85+
})
86+
qt.Assert(tb, qt.IsNil(err))
87+
tb.Cleanup(func() {
88+
prog.Close()
89+
})
90+
91+
return prog, events
92+
}

ringbuf/helper_windows_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package ringbuf
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-quicktest/qt"
7+
8+
"github.com/cilium/ebpf"
9+
"github.com/cilium/ebpf/asm"
10+
)
11+
12+
func mustOutputSamplesProg(tb testing.TB, sampleMessages ...sampleMessage) (*ebpf.Program, *ebpf.Map) {
13+
tb.Helper()
14+
15+
events, err := ebpf.NewMap(&ebpf.MapSpec{
16+
Type: ebpf.WindowsRingBuf,
17+
MaxEntries: 4096,
18+
})
19+
qt.Assert(tb, qt.IsNil(err))
20+
tb.Cleanup(func() {
21+
events.Close()
22+
})
23+
24+
var maxSampleSize int
25+
for _, sampleMessage := range sampleMessages {
26+
if sampleMessage.size > maxSampleSize {
27+
maxSampleSize = sampleMessage.size
28+
}
29+
}
30+
31+
insns := asm.Instructions{
32+
asm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord),
33+
asm.Mov.Reg(asm.R9, asm.R1),
34+
}
35+
36+
bufDwords := (maxSampleSize / 8) + 1
37+
for i := range bufDwords {
38+
insns = append(insns,
39+
asm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord),
40+
)
41+
}
42+
43+
for _, sampleMessage := range sampleMessages {
44+
if sampleMessage.discard {
45+
tb.Skip("discard is not supported on Windows")
46+
}
47+
48+
insns = append(insns,
49+
asm.LoadMapPtr(asm.R1, events.FD()),
50+
asm.Mov.Reg(asm.R2, asm.RFP),
51+
asm.Add.Imm(asm.R2, -int32(8*bufDwords)),
52+
asm.Mov.Imm(asm.R3, int32(sampleMessage.size)),
53+
asm.Mov.Imm(asm.R4, sampleMessage.flags),
54+
asm.WindowsFnRingbufOutput.Call(),
55+
asm.JNE.Imm(asm.R0, 0, "exit"),
56+
)
57+
}
58+
59+
insns = append(insns,
60+
asm.Mov.Imm(asm.R0, int32(0)),
61+
asm.Return().WithSymbol("exit"),
62+
)
63+
64+
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
65+
License: "MIT",
66+
Type: ebpf.WindowsXDPTest,
67+
Instructions: insns,
68+
})
69+
qt.Assert(tb, qt.IsNil(err))
70+
tb.Cleanup(func() {
71+
prog.Close()
72+
})
73+
74+
return prog, events
75+
}

0 commit comments

Comments
 (0)