Skip to content

Commit be582b9

Browse files
committed
Support ring buffers by introducing new types and operations. Update AST, IR generation, and code generation to handle ring buffer declarations, operations, and validations. Refactor map handling to accommodate ring buffers, ensuring compatibility with existing map structures.
1 parent 4248f03 commit be582b9

28 files changed

Lines changed: 2890 additions & 255 deletions

SPEC.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,11 +3406,14 @@ fn transparent_dynptr_usage(event_data: *u8, data_len: u32) {
34063406
}
34073407
}
34083408
3409-
// What compiler generates (conceptually):
3409+
// What compiler generates (using modern dynptr APIs):
34103410
// - bpf_ringbuf_reserve_dynptr() for allocation
3411-
// - bpf_dynptr_data() for direct access when possible
3412-
// - bpf_dynptr_write() for safe access when needed
3411+
// - bpf_dynptr_data() for pointer retrieval
3412+
// - bpf_dynptr_write() for ALL field assignments (event->field = value)
34133413
// - bpf_ringbuf_submit_dynptr() for submission
3414+
// Example: event->id = 42 becomes:
3415+
// { __u32 __tmp_val = 42;
3416+
// bpf_dynptr_write(&event_dynptr, __builtin_offsetof(struct Event, id), &__tmp_val, 4, 0); }
34143417
```
34153418

34163419
### 10.3 Stack Management and Large Struct Handling

examples/ringbuf_demo.ks

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Ring Buffer Demonstration for KernelScript
2+
// Shows complete ring buffer API usage from eBPF to userspace
3+
4+
// XDP context struct (from BTF)
5+
struct xdp_md {
6+
data: u64,
7+
data_end: u64,
8+
data_meta: u64,
9+
ingress_ifindex: u32,
10+
rx_queue_index: u32,
11+
egress_ifindex: u32,
12+
}
13+
14+
// XDP action enum (from BTF)
15+
enum xdp_action {
16+
XDP_ABORTED = 0,
17+
XDP_DROP = 1,
18+
XDP_PASS = 2,
19+
XDP_REDIRECT = 3,
20+
XDP_TX = 4,
21+
}
22+
23+
// Event structures for different types of events
24+
struct NetworkEvent {
25+
timestamp: u64,
26+
event_type: u32,
27+
src_ip: u32,
28+
dst_ip: u32,
29+
port: u16,
30+
protocol: u8,
31+
packet_size: u16,
32+
}
33+
34+
struct SecurityEvent {
35+
timestamp: u64,
36+
severity: u32,
37+
event_id: u32,
38+
pid: u32,
39+
message: u8[64],
40+
}
41+
42+
// Ring buffer declarations
43+
var network_events : ringbuf<NetworkEvent>(8192) // 8KB ring buffer
44+
pin var security_events : ringbuf<SecurityEvent>(16384) // 16KB pinned ring buffer
45+
46+
// Stats for monitoring
47+
struct Stats {
48+
events_submitted: u64,
49+
events_dropped: u64,
50+
buffer_full_count: u64,
51+
}
52+
53+
var stats : hash<u32, Stats>(1)
54+
55+
@helper
56+
fn get_timestamp() -> u64 {
57+
return 1234567890 // Demo timestamp - would be bpf_ktime_get_ns() in real code
58+
}
59+
60+
@helper
61+
fn get_packet_info(ctx: *xdp_md) -> NetworkEvent {
62+
var event = NetworkEvent {
63+
timestamp: get_timestamp(),
64+
event_type: 1, // PACKET_RECEIVED
65+
src_ip: 0x7f000001, // 127.0.0.1
66+
dst_ip: 0x7f000002, // 127.0.0.2
67+
port: 80,
68+
protocol: 6, // TCP
69+
packet_size: 64,
70+
}
71+
return event
72+
}
73+
74+
// XDP program that generates network events
75+
@xdp fn network_monitor(ctx: *xdp_md) -> xdp_action {
76+
var key: u32 = 0
77+
var stat = stats[key]
78+
if (stat == none) {
79+
var init_stat = Stats { events_submitted: 0, events_dropped: 0, buffer_full_count: 0 }
80+
stats[key] = init_stat
81+
stat = stats[key]
82+
}
83+
84+
// Try to reserve space in ring buffer
85+
var reserved = network_events.reserve()
86+
if (reserved != null) {
87+
// Successfully reserved space - build event and submit
88+
// For now, just submit the reserved space
89+
network_events.submit(reserved)
90+
stat.events_submitted = stat.events_submitted + 1
91+
} else {
92+
// Ring buffer is full - increment drop counter
93+
stat.events_dropped = stat.events_dropped + 1
94+
stat.buffer_full_count = stat.buffer_full_count + 1
95+
}
96+
97+
return XDP_PASS
98+
}
99+
100+
// Security monitoring program
101+
@kprobe fn security_monitor() -> i32 {
102+
var reserved = security_events.reserve()
103+
if (reserved != null) {
104+
// Successfully reserved space - submit the event
105+
security_events.submit(reserved)
106+
} else {
107+
// Handle full buffer - could discard or try alternative logging
108+
// Note: discard not needed for failed reserve
109+
}
110+
111+
return 0
112+
}
113+
114+
// Userspace event handling
115+
116+
// Event handler for network events
117+
fn network_event_handler(event: *NetworkEvent) -> i32 {
118+
print("Network Event:")
119+
print(" Timestamp: ", event->timestamp)
120+
print(" Type: ", event->event_type)
121+
print(" Source IP: ", event->src_ip)
122+
print(" Destination IP: ", event->dst_ip)
123+
print(" Port: ", event->port)
124+
print(" Protocol: ", event->protocol)
125+
print(" Packet Size: ", event->packet_size)
126+
return 0
127+
}
128+
129+
// Event handler for security events
130+
fn security_event_handler(event: *SecurityEvent) -> i32 {
131+
print("Security Event:")
132+
print(" Timestamp: ", event->timestamp)
133+
print(" Severity: ", event->severity)
134+
print(" Event ID: ", event->event_id)
135+
print(" PID: ", event->pid)
136+
print(" Message: [security event]")
137+
return 0
138+
}
139+
140+
// Custom callback functions (override weak symbols)
141+
fn network_events_callback(event: *NetworkEvent) -> i32 {
142+
return network_event_handler(event)
143+
}
144+
145+
fn security_events_callback(event: *SecurityEvent) -> i32 {
146+
return security_event_handler(event)
147+
}
148+
149+
// Main userspace program
150+
fn main() -> i32 {
151+
print("Starting ring buffer demonstration...")
152+
153+
// Load and attach eBPF programs
154+
var network_prog = load(network_monitor)
155+
var security_prog = load(security_monitor)
156+
157+
if (network_prog == null || security_prog == null) {
158+
print("Failed to load eBPF programs")
159+
return 1
160+
}
161+
162+
// Attach programs
163+
var net_result = attach(network_prog, "eth0", 0) // Attach XDP to eth0
164+
var sec_result = attach(security_prog, "sys_openat", 0) // Attach kprobe to sys_openat
165+
166+
if (net_result != 0 || sec_result != 0) {
167+
print("Failed to attach eBPF programs")
168+
return 1
169+
}
170+
171+
print("eBPF programs loaded and attached successfully")
172+
print("Starting event processing...")
173+
print("Press Ctrl+C to stop")
174+
175+
// Start processing ring buffer events using the builtin dispatch() function
176+
dispatch(network_events, security_events)
177+
178+
return 0
179+
}
180+
181+
// Utility function to get statistics
182+
fn print_stats() -> i32 {
183+
print("=== Ring Buffer Statistics ===")
184+
// In a real implementation, would read from stats map
185+
print("Network events processed: [would read from eBPF map]")
186+
print("Security events processed: [would read from eBPF map]")
187+
print("Buffer full events: [would read from eBPF map]")
188+
return 0
189+
}

examples/ringbuf_on_event_demo.ks

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Ring Buffer on_event() Demo
2+
// Shows how to register event handlers for ring buffers
3+
4+
// XDP context struct (from BTF)
5+
struct xdp_md {
6+
data: u64,
7+
data_end: u64,
8+
data_meta: u64,
9+
ingress_ifindex: u32,
10+
rx_queue_index: u32,
11+
egress_ifindex: u32,
12+
}
13+
14+
// XDP action enum (from BTF)
15+
enum xdp_action {
16+
XDP_ABORTED = 0,
17+
XDP_DROP = 1,
18+
XDP_PASS = 2,
19+
XDP_REDIRECT = 3,
20+
XDP_TX = 4,
21+
}
22+
23+
struct NetworkEvent {
24+
src_ip: u32,
25+
dst_ip: u32,
26+
packet_size: u16,
27+
protocol: u8,
28+
}
29+
30+
struct SecurityEvent {
31+
event_type: u32,
32+
severity: u8,
33+
timestamp: u64,
34+
}
35+
36+
// Ring buffer declarations
37+
var network_events : ringbuf<NetworkEvent>(4096)
38+
var security_events : ringbuf<SecurityEvent>(8192)
39+
40+
@xdp fn network_monitor(ctx: *xdp_md) -> xdp_action {
41+
var reserved = network_events.reserve()
42+
network_events.submit(reserved)
43+
return XDP_PASS
44+
}
45+
46+
@kprobe fn security_monitor(ctx: *pt_regs) -> i32 {
47+
var reserved = security_events.reserve()
48+
security_events.submit(reserved)
49+
return 0
50+
}
51+
52+
// Event handler functions
53+
fn handle_network_event(event: *NetworkEvent) -> i32 {
54+
print("Network event received")
55+
return 0
56+
}
57+
58+
fn handle_security_event(event: *SecurityEvent) -> i32 {
59+
print("Security event received")
60+
return 0
61+
}
62+
63+
fn main() -> i32 {
64+
print("Starting ring buffer on_event demo")
65+
66+
// Register event handlers with ring buffers
67+
network_events.on_event(handle_network_event)
68+
security_events.on_event(handle_security_event)
69+
70+
// Load and attach programs
71+
var net_prog = load(network_monitor)
72+
var sec_prog = load(security_monitor)
73+
74+
// Start event processing for both ring buffers
75+
dispatch(network_events, security_events)
76+
77+
return 0
78+
}

src/ast.ml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ and bpf_type =
7171
| Option of bpf_type
7272
| Result of bpf_type * bpf_type
7373
| Function of bpf_type list * bpf_type
74-
| Map of bpf_type * bpf_type * map_type
74+
| Map of bpf_type * bpf_type * map_type * int (* key_type, value_type, map_type, size *)
7575
(* Built-in context types *)
7676
| Xdp_md | UprobeContext
7777
| TracepointContext | LsmContext | CgroupSkbContext
@@ -80,6 +80,9 @@ and bpf_type =
8080
| ProgramRef of program_type
8181
(* Program handle type - represents a loaded program *)
8282
| ProgramHandle
83+
(* Ring buffer reference type - represents a ring buffer for dispatch *)
84+
| RingbufRef of bpf_type (* value type *)
85+
| Ringbuf of bpf_type * int (* value_type, size - ring buffer object *)
8386
(* None type - represents missing/absent values *)
8487
| NoneType
8588

@@ -594,11 +597,12 @@ let rec string_of_bpf_type = function
594597
Printf.sprintf "function (%s) -> %s"
595598
(String.concat ", " (List.map string_of_bpf_type params))
596599
(string_of_bpf_type return_type)
597-
| Map (key_type, value_type, map_type) ->
598-
Printf.sprintf "map (%s, %s, %s)"
600+
| Map (key_type, value_type, map_type, size) ->
601+
Printf.sprintf "map (%s, %s, %s, %d)"
599602
(string_of_bpf_type key_type)
600603
(string_of_bpf_type value_type)
601604
(string_of_map_type map_type)
605+
size
602606
| Xdp_md -> "xdp_md"
603607
| UprobeContext -> "UprobeContext"
604608
| TracepointContext -> "TracepointContext"
@@ -607,6 +611,8 @@ let rec string_of_bpf_type = function
607611
| Xdp_action -> "xdp_action"
608612
| ProgramRef pt -> string_of_program_type pt
609613
| ProgramHandle -> "ProgramHandle"
614+
| RingbufRef value_type -> Printf.sprintf "ringbuf_ref<%s>" (string_of_bpf_type value_type)
615+
| Ringbuf (value_type, size) -> Printf.sprintf "ringbuf<%s>(%d)" (string_of_bpf_type value_type) size
610616
| NoneType -> "none"
611617

612618
let rec string_of_literal = function

0 commit comments

Comments
 (0)