Skip to content

Commit cd32346

Browse files
committed
Enhance TC program support by requiring direction specification.
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent fd70390 commit cd32346

32 files changed

+340
-99
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ KernelScript supports all major eBPF program types with typed contexts:
8686
}
8787
8888
// TC program for traffic control
89-
@tc fn traffic_shaper(ctx: *__sk_buff) -> i32 {
89+
@tc("ingress")
90+
fn traffic_shaper(ctx: *__sk_buff) -> i32 {
9091
if (ctx->len > 1000) {
9192
return TC_ACT_SHOT // Drop large packets
9293
}
@@ -212,7 +213,8 @@ pin var shared_counter : hash<u32, u32>(1024)
212213
}
213214
214215
// TC program reads counter
215-
@tc fn packet_reader(ctx: *__sk_buff) -> int {
216+
@tc("ingress")
217+
fn packet_reader(ctx: *__sk_buff) -> int {
216218
var count = shared_counter[1]
217219
if (count > 1000) {
218220
return TC_ACT_SHOT // Rate limiting
@@ -245,7 +247,7 @@ Create a new KernelScript project with template code:
245247
kernelscript init xdp my_packet_filter
246248

247249
# Create TC project
248-
kernelscript init tc my_traffic_shaper
250+
kernelscript init tc/egress my_traffic_shaper
249251

250252
# Create probe project
251253
kernelscript init probe/sys_read my_tracer

SPEC.md

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn monitor(ctx: *xdp_md) -> xdp_action {
6868
return XDP_PASS
6969
}
7070
71-
@tc
71+
@tc("ingress")
7272
fn analyzer(ctx: *__sk_buff) -> int {
7373
update_counters(1) // Same kernel-shared function
7474
return 0 // TC_ACT_OK
@@ -217,9 +217,49 @@ fn arbitrary_address() -> i32 {
217217
**Key Benefits:**
218218
- **Intelligent Probe Selection**: Automatically chooses fprobe for function entrance (better performance) or kprobe for arbitrary addresses
219219
- **Type Safety**: Function entrance probes have correct types extracted from kernel BTF information
220-
- **No Magic Numbers**: Direct parameter access for function entrance probes
221-
- **Self-Documenting**: Function signature matches the actual kernel function for entrance probes
222-
- **Compile-Time Validation**: Invalid parameter access caught at compile time
220+
221+
#### 3.1.2 Traffic Control (TC) Programs with Direction Support
222+
223+
TC programs must specify traffic direction for proper kernel attachment point selection.
224+
225+
```kernelscript
226+
// Ingress traffic control (packets entering the interface)
227+
@tc("ingress")
228+
fn ingress_filter(ctx: *__sk_buff) -> int {
229+
var packet_size = ctx->len
230+
231+
// Drop oversized packets at ingress
232+
if (packet_size > 1500) {
233+
return TC_ACT_SHOT // Drop packet
234+
}
235+
236+
return TC_ACT_OK // Allow packet
237+
}
238+
239+
// Egress traffic control (packets leaving the interface)
240+
@tc("egress")
241+
fn egress_shaper(ctx: *__sk_buff) -> int {
242+
var protocol = ctx->protocol
243+
244+
// Shape traffic based on protocol at egress
245+
if (protocol == ETH_P_IP) {
246+
// Apply rate limiting logic
247+
return TC_ACT_PIPE // Continue processing
248+
}
249+
250+
return TC_ACT_OK // Allow packet
251+
}
252+
```
253+
254+
**TC Direction Specification:**
255+
- **@tc("ingress")**: Attaches to ingress hook (packets entering interface)
256+
- **@tc("egress")**: Attaches to egress hook (packets leaving interface)
257+
- Direction parameter is **required** - no default direction is assumed
258+
259+
**Key Benefits:**
260+
- **Explicit Direction Control**: Clear specification of traffic direction for precise attachment
261+
- **Type Safety**: All TC programs use standard __sk_buff context with compile-time validation
262+
- **Kernel Integration**: Direct mapping to kernel TC ingress/egress hooks
223263

224264
**Probe Type Selection:**
225265
- `@probe("function_name")` → Uses **fprobe** for function entrance with direct parameter access
@@ -726,7 +766,7 @@ fn packet_analyzer(ctx: *xdp_md) -> xdp_action {
726766
return XDP_PASS
727767
}
728768
729-
@tc
769+
@tc("ingress")
730770
fn flow_tracker(ctx: *__sk_buff) -> int {
731771
// Track flow information using shared config
732772
if (monitoring.enable_stats && (ctx.hash() % monitoring.sample_rate == 0)) {
@@ -795,7 +835,7 @@ fn packet_filter(ctx: *xdp_md) -> xdp_action {
795835
return XDP_PASS
796836
}
797837
798-
@tc
838+
@tc("ingress")
799839
fn flow_monitor(ctx: *__sk_buff) -> int {
800840
return 0 // TC_ACT_OK
801841
}
@@ -827,7 +867,7 @@ fn main() -> i32 {
827867
- First parameter must be a ProgramHandle returned from load()
828868
- Target and flags interpretation depends on program type:
829869
- **XDP**: target = interface name ("eth0"), flags = XDP attachment flags
830-
- **TC**: target = interface name ("eth0"), flags = direction (ingress/egress)
870+
- **TC**: target = interface name ("eth0"), direction determined from @tc("ingress"/"egress") attribute
831871
- **Kprobe**: target = function name ("sys_read"), flags = unused (0)
832872
- **Cgroup**: target = cgroup path ("/sys/fs/cgroup/test"), flags = unused (0)
833873
- Returns 0 on success, negative error code on failure
@@ -949,7 +989,7 @@ fn main(args: Args) -> i32 {
949989
@xdp
950990
fn ingress_monitor(ctx: *xdp_md) -> xdp_action { return XDP_PASS }
951991
952-
@tc
992+
@tc("egress")
953993
fn egress_monitor(ctx: *__sk_buff) -> int { return 0 } // TC_ACT_OK
954994
955995
// Struct_ops example using impl block approach
@@ -1245,7 +1285,7 @@ fn packet_filter(ctx: *xdp_md) -> xdp_action {
12451285
return XDP_PASS
12461286
}
12471287
1248-
@tc
1288+
@tc("ingress")
12491289
fn traffic_shaper(ctx: *__sk_buff) -> int {
12501290
var packet = ctx.packet()
12511291
@@ -1301,7 +1341,7 @@ fn ddos_protection(ctx: *xdp_md) -> xdp_action {
13011341
return XDP_PASS
13021342
}
13031343
1304-
@tc
1344+
@tc("ingress")
13051345
fn connection_tracker(ctx: *__sk_buff) -> int {
13061346
var tcp_info = extract_tcp_info(ctx) // Reuse same helper
13071347
if (tcp_info != null) {
@@ -1435,7 +1475,7 @@ fn high_level_filter(packet: *u8, len: u32) -> i32 {
14351475
}
14361476
14371477
// eBPF usage
1438-
@tc
1478+
@tc("ingress")
14391479
fn traffic_analyzer(ctx: *__sk_buff) -> int {
14401480
var packet = ctx.packet()
14411481
@@ -2342,7 +2382,7 @@ fn ingress_monitor(ctx: *xdp_md) -> xdp_action {
23422382
}
23432383
23442384
// Program 2: Automatically has access to the same global maps
2345-
@tc
2385+
@tc("egress")
23462386
fn egress_monitor(ctx: *__sk_buff) -> int {
23472387
var flow_key = extract_flow_key(ctx)?
23482388
@@ -2802,7 +2842,7 @@ fn packet_filter(ctx: *xdp_md) -> xdp_action {
28022842
return XDP_PASS
28032843
}
28042844
2805-
@tc
2845+
@tc("ingress")
28062846
fn flow_monitor(ctx: *__sk_buff) -> int {
28072847
// Can call the same kernel-shared functions
28082848
if (!validate_packet(ctx.packet())) {
@@ -2907,7 +2947,7 @@ fn main_filter(ctx: *xdp_md) -> xdp_action {
29072947
return specialized_filter(ctx) // ✅ Same type (@xdp), return position
29082948
}
29092949
2910-
@tc
2950+
@tc("ingress")
29112951
fn ingress_handler(ctx: *__sk_buff) -> int {
29122952
return security_check(ctx) // ✅ Same type (@tc), return position
29132953
}
@@ -4016,7 +4056,7 @@ mod program {
40164056
// Attach a program to a target with optional flags using its handle
40174057
// - First parameter must be a ProgramHandle returned from load()
40184058
// - For XDP: target is interface name (e.g., "eth0"), flags are XDP attachment flags
4019-
// - For TC: target is interface name, flags indicate direction (ingress/egress)
4059+
// - For TC: target is interface name, direction determined from @tc attribute
40204060
// - For Kprobe: target is function name (e.g., "sys_read"), flags are unused (0)
40214061
// - For Cgroup: target is cgroup path (e.g., "/sys/fs/cgroup/test"), flags are unused (0)
40224062
pub fn attach(handle: ProgramHandle, target: string, flags: u32) -> u32
@@ -4120,7 +4160,7 @@ fn simple_filter(ctx: *xdp_md) -> xdp_action {
41204160
return XDP_PASS
41214161
}
41224162
4123-
@tc
4163+
@tc("ingress")
41244164
fn security_monitor(ctx: *__sk_buff) -> int {
41254165
var packet = ctx.packet()
41264166
if (packet == null) {

examples/map_operations_demo.ks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ struct ArrayElement {
139139
}
140140

141141
// Program 2: Writer workload demonstrating conflict detection
142-
@tc fn stats_updater(ctx: *__sk_buff) -> i32 {
142+
@tc("ingress")
143+
fn stats_updater(ctx: *__sk_buff) -> i32 {
143144
var ifindex = ctx->ifindex
144145

145146
// Potential write conflict with other programs

examples/maps_demo.ks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ fn get_timestamp() -> u64 {
128128
}
129129

130130
// TC program demonstrating different map usage patterns
131-
@tc fn traffic_shaper(ctx: *__sk_buff) -> int {
131+
@tc("ingress")
132+
fn traffic_shaper(ctx: *__sk_buff) -> int {
132133
var cpu = get_cpu_id()
133134
var bytes = get_packet_len_tc(ctx)
134135

examples/multi_programs.ks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ pin var shared_counter : hash<u32, u32>(1024)
4848
return XDP_PASS
4949
}
5050

51-
@tc fn packet_filter(ctx: *__sk_buff) -> i32 {
51+
@tc("ingress")
52+
fn packet_filter(ctx: *__sk_buff) -> i32 {
5253
shared_counter[2] = 200
5354
return TC_ACT_OK
5455
}

examples/packet_matching.ks

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ fn packet_monitor(ctx: *xdp_md) -> xdp_action {
283283

284284
// Quality of Service (QoS) packet marking
285285
// Demonstrates match for traffic prioritization
286-
@tc
286+
@tc("ingress")
287287
fn qos_packet_marker(ctx: *__sk_buff) -> int {
288288
var protocol = get_ip_protocol_tc(ctx)
289289

examples/symbols.ks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ fn log_packet(info: PacketInfo) -> u32 {
9191
}
9292
}
9393

94-
@tc fn traffic_monitor(ctx: *__sk_buff) -> i32 {
94+
@tc("ingress")
95+
fn traffic_monitor(ctx: *__sk_buff) -> i32 {
9596
var packet_protocol = ctx->protocol
9697

9798
// Access global map (visible from all programs)

src/btf_parser.ml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ let generate_kprobe_function_from_signature func_name signature =
284284
sprintf "fn %s(%s) -> %s" func_name params_str return_type
285285

286286
(** Generate KernelScript source code from template *)
287-
let generate_kernelscript_source template project_name =
287+
let generate_kernelscript_source ?extra_param template project_name =
288288
let context_comment = match template.program_type with
289289
| "xdp" -> "// XDP (eXpress Data Path) program for high-performance packet processing"
290290
| "tc" -> "// TC (Traffic Control) program for network traffic shaping and filtering"
@@ -362,6 +362,12 @@ let generate_kernelscript_source template project_name =
362362
let func_def = sprintf "fn %s_handler(ctx: %s) -> %s"
363363
(String.map (function '/' -> '_' | c -> c) first_event) template.context_type template.return_type in
364364
(comment, first_event, func_def, Some (sprintf "@tracepoint(\"%s\")" first_event))
365+
else if template.program_type = "tc" && extra_param <> None then
366+
let direction = match extra_param with Some d -> d | None -> "ingress" in
367+
let comment = sprintf "\n// TC %s traffic control program\n" direction in
368+
let func_name = sprintf "%s_%s_handler" project_name direction in
369+
let func_def = sprintf "fn %s(ctx: %s) -> %s" func_name template.context_type template.return_type in
370+
(comment, direction, func_def, Some (sprintf "@tc(\"%s\")" direction))
365371
else
366372
("", "target_function", sprintf "fn %s_handler(ctx: %s) -> %s" project_name template.context_type template.return_type, None)
367373
in
@@ -391,9 +397,19 @@ let generate_kernelscript_source template project_name =
391397
if template.program_type = "kprobe" then target_function_name
392398
else if template.program_type = "tracepoint" then
393399
String.map (function '/' -> '_' | c -> c) target_function_name ^ "_handler"
400+
else if template.program_type = "tc" && extra_param <> None then
401+
let direction = match extra_param with Some d -> d | None -> "ingress" in
402+
sprintf "%s_%s_handler" project_name direction
394403
else sprintf "%s_handler" project_name
395404
in
396405

406+
let program_description =
407+
if template.program_type = "tc" && extra_param <> None then
408+
let direction = match extra_param with Some d -> d | None -> "ingress" in
409+
sprintf "TC %s" direction
410+
else template.program_type
411+
in
412+
397413
sprintf {|%s
398414
// Generated by KernelScript compiler with direct BTF parsing
399415
%s
@@ -421,4 +437,4 @@ fn main() -> i32 {
421437

422438
return 0
423439
}
424-
|} context_comment function_signatures_comment type_definitions attribute_line function_definition template.program_type sample_return function_name attach_comment attach_target template.program_type template.program_type
440+
|} context_comment function_signatures_comment type_definitions attribute_line function_definition template.program_type sample_return function_name attach_comment attach_target program_description program_description

src/btf_parser.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ val extract_struct_ops_definitions : string option -> string list -> string list
5252
val generate_struct_ops_template : string option -> string list -> string -> string
5353

5454
(** Generate KernelScript source code from template *)
55-
val generate_kernelscript_source : program_template -> string -> string
55+
val generate_kernelscript_source : ?extra_param:string -> program_template -> string -> string

src/context/context_codegen.ml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type context_codegen = {
4343
generate_field_access: string -> string -> string; (* ctx_var -> field_name -> C expression *)
4444
map_action_constant: int -> string option; (* Map integer to action constant *)
4545
generate_function_signature: (string -> (string * string) list -> string -> string) option; (* func_name -> parameters -> return_type -> signature *)
46+
generate_section_name: (string option -> string) option; (* Optional function to generate SEC(...) attribute with target *)
4647
}
4748

4849
(** Registry for context code generators *)
@@ -232,6 +233,7 @@ let create_context_codegen_from_btf ctx_type_name btf_type_info =
232233
generate_field_access;
233234
map_action_constant;
234235
generate_function_signature = None;
236+
generate_section_name = None;
235237
}
236238

237239
(** Register context codegen from BTF type information *)
@@ -272,4 +274,13 @@ let update_context_codegen_with_btf ctx_type_name btf_type_info =
272274
ctx_type_name (List.length btf_only_fields)
273275
| None ->
274276
(* No existing codegen, create new one from BTF *)
275-
register_btf_context_codegen ctx_type_name btf_type_info
277+
register_btf_context_codegen ctx_type_name btf_type_info
278+
279+
(** Generate section name for a context type with optional direction *)
280+
let generate_context_section_name ctx_type direction =
281+
match get_context_codegen ctx_type with
282+
| Some codegen ->
283+
(match codegen.generate_section_name with
284+
| Some section_fn -> Some (section_fn direction)
285+
| None -> None)
286+
| None -> None

0 commit comments

Comments
 (0)