Skip to content

Commit 3fa7e90

Browse files
committed
Add support for extern kernel function declarations in KernelScript.
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent d16dd57 commit 3fa7e90

File tree

10 files changed

+468
-15
lines changed

10 files changed

+468
-15
lines changed

SPEC.md

Lines changed: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ pin type struct enum if else
141141
while loop break continue return import
142142
pub priv impl true false null
143143
try catch throw defer delete match
144+
extern
144145
```
145146

146147
**Note**: The `pin` keyword is used for both maps and global variables to enable filesystem persistence.
@@ -1240,11 +1241,113 @@ fn advanced_security_monitor(ctx: LsmContext) -> i32 {
12401241
}
12411242
```
12421243

1243-
### 3.7 Helper Functions (@helper)
1244+
### 3.7 External Kernel Functions (extern)
1245+
1246+
KernelScript supports importing existing kernel functions using the `extern` keyword. These are kernel functions that already exist in the running kernel (discovered via BTF) and can be called directly from eBPF programs without requiring custom kernel modules.
1247+
1248+
#### 3.7.1 extern Declaration and Usage
1249+
1250+
External kernel functions are declared using the `extern` keyword and provide type-safe access to kernel-provided kfuncs:
1251+
1252+
```kernelscript
1253+
// Import existing kernel functions via extern declarations
1254+
extern bpf_ktime_get_ns() -> u64
1255+
extern bpf_trace_printk(fmt: *u8, fmt_size: u32) -> i32
1256+
extern bpf_get_current_pid_tgid() -> u64
1257+
extern bpf_get_current_comm(buf: *u8, buf_size: u32) -> i32
1258+
1259+
// eBPF programs can call extern functions directly
1260+
@xdp
1261+
fn packet_tracer(ctx: *xdp_md) -> xdp_action {
1262+
// Get current timestamp using extern kfunc
1263+
var timestamp = bpf_ktime_get_ns()
1264+
1265+
// Get current process ID using extern kfunc
1266+
var pid_tgid = bpf_get_current_pid_tgid()
1267+
var pid = (pid_tgid >> 32) as u32
1268+
1269+
// Get process name
1270+
var comm: u8[16]
1271+
bpf_get_current_comm(&comm[0], 16)
1272+
1273+
// Print debug information
1274+
bpf_trace_printk(&"packet from pid %d\n"[0], 18)
1275+
1276+
return XDP_PASS
1277+
}
1278+
```
1279+
1280+
#### 3.7.2 extern vs @kfunc Comparison
1281+
1282+
| Aspect | `extern` | `@kfunc` |
1283+
|--------|----------|----------|
1284+
| **Definition** | Declaration of existing kernel function | User-defined kernel function |
1285+
| **Implementation** | Already exists in kernel | Implemented in generated kernel module |
1286+
| **BTF Registration** | Already registered | Registered by compiler |
1287+
| **Compilation** | Declaration only | Full implementation + module |
1288+
| **Usage** | Import existing kernel APIs | Create custom kernel functionality |
1289+
| **Performance** | Direct kernel function call | BTF-mediated call to module |
1290+
1291+
#### 3.7.3 extern Declaration Rules
1292+
1293+
- **Declaration only**: `extern` functions must not have function bodies
1294+
- **Type safety**: Parameter and return types must match kernel BTF signatures
1295+
- **eBPF only**: `extern` functions can only be called from eBPF programs, not userspace
1296+
- **Kernel availability**: Functions must exist in the target kernel version
1297+
1298+
```kernelscript
1299+
// ✅ Valid extern declaration
1300+
extern bpf_ktime_get_ns() -> u64
1301+
1302+
// ❌ Invalid - extern cannot have function body
1303+
extern invalid_function() -> u32 {
1304+
return 42 // Error: extern functions cannot have bodies
1305+
}
1306+
1307+
// ❌ Invalid - extern functions cannot be called from userspace
1308+
fn userspace_function() -> u64 {
1309+
return bpf_ktime_get_ns() // Error: extern kfuncs only callable from eBPF
1310+
}
1311+
```
1312+
1313+
#### 3.7.4 BTF Integration and Discovery
1314+
1315+
The compiler can automatically discover available kernel functions from BTF:
1316+
1317+
```bash
1318+
# Automatic extern generation from kernel BTF
1319+
kernelscript init --kfuncs xdp my_xdp
1320+
1321+
# Generated extern_kfuncs.ks would contain:
1322+
# extern bpf_ktime_get_ns() -> u64
1323+
# extern bpf_trace_printk(fmt: *u8, fmt_size: u32) -> i32
1324+
# extern bpf_get_current_pid_tgid() -> u64
1325+
# ... (all available kernel kfuncs)
1326+
```
1327+
1328+
#### 3.7.5 Common extern kfunc Examples
1329+
1330+
```kernelscript
1331+
extern bpf_ktime_get_ns() -> u64
1332+
extern bpf_get_current_pid_tgid() -> u64
1333+
extern bpf_trace_printk(fmt: *u8, fmt_size: u32) -> i32
1334+
1335+
@tc("ingress")
1336+
fn network_monitor(ctx: *__sk_buff) -> i32 {
1337+
var timestamp = bpf_ktime_get_ns()
1338+
var pid_tgid = bpf_get_current_pid_tgid()
1339+
1340+
// Process monitoring logic here
1341+
bpf_trace_printk("Processing packet at %llu from PID %d\n", 40)
1342+
return 0 // TC_ACT_OK
1343+
}
1344+
```
1345+
1346+
### 3.8 Helper Functions (@helper)
12441347

12451348
KernelScript supports kernel-shared helper functions using the `@helper` attribute. These functions compile to eBPF bytecode and are shared across all eBPF programs within the same compilation unit, providing a way to reuse common logic without duplicating code.
12461349

1247-
#### 3.7.1 @helper Declaration and Usage
1350+
#### 3.8.1 @helper Declaration and Usage
12481351

12491352
Helper functions are declared using the `@helper` attribute and can be called from any eBPF program:
12501353

@@ -1301,7 +1404,7 @@ fn traffic_shaper(ctx: *__sk_buff) -> int {
13011404
}
13021405
```
13031406

1304-
#### 3.7.2 @helper vs Other Function Types
1407+
#### 3.8.2 @helper vs Other Function Types
13051408

13061409
| Aspect | `@helper` | `@kfunc` | `@xdp/@tc/etc` | Regular `fn` |
13071410
|--------|-----------|----------|----------------|--------------|
@@ -1311,7 +1414,7 @@ fn traffic_shaper(ctx: *__sk_buff) -> int {
13111414
| **Shared Across Programs** | Yes | Yes | No | No |
13121415
| **Memory Access** | eBPF-restricted | Unrestricted kernel | eBPF-restricted | Userspace-restricted |
13131416

1314-
#### 3.7.3 Code Organization Benefits
1417+
#### 3.8.3 Code Organization Benefits
13151418

13161419
Using `@helper` functions provides several benefits:
13171420

@@ -1351,11 +1454,11 @@ fn connection_tracker(ctx: *__sk_buff) -> int {
13511454
}
13521455
```
13531456

1354-
### 3.8 Private Kernel Module Functions (@private)
1457+
### 3.9 Private Kernel Module Functions (@private)
13551458

13561459
KernelScript supports private helper functions within kernel modules using the `@private` attribute. These functions execute in kernel space but are internal to the module - they cannot be called by eBPF programs and are not registered via BTF. They serve as utility functions for `@kfunc` implementations.
13571460

1358-
#### 3.8.1 @private Declaration and Usage
1461+
#### 3.9.1 @private Declaration and Usage
13591462

13601463
Private functions are declared using the `@private` attribute and can only be called by other functions within the same kernel module:
13611464

@@ -1446,7 +1549,7 @@ fn packet_filter(ctx: *xdp_md) -> xdp_action {
14461549
}
14471550
```
14481551

1449-
#### 3.8.2 Function Visibility and Call Hierarchy
1552+
#### 3.9.2 Function Visibility and Call Hierarchy
14501553

14511554
```kernelscript
14521555
// Example showing function call hierarchy
@@ -1486,7 +1589,7 @@ fn traffic_analyzer(ctx: *__sk_buff) -> int {
14861589
}
14871590
```
14881591

1489-
#### 3.8.3 @private vs @kfunc Comparison
1592+
#### 3.9.3 @private vs @kfunc Comparison
14901593

14911594
| Aspect | `@private` | `@kfunc` |
14921595
|--------|-----------|----------|
@@ -1497,7 +1600,7 @@ fn traffic_analyzer(ctx: *__sk_buff) -> int {
14971600
| **Use Case** | Internal implementation details | Public API functions |
14981601
| **Performance** | Direct function call | BTF-mediated call |
14991602

1500-
#### 3.8.4 Code Organization Benefits
1603+
#### 3.9.4 Code Organization Benefits
15011604

15021605
Using `@private` functions provides several architectural benefits:
15031606

@@ -1560,11 +1663,11 @@ fn optimized_packet_check(packet: *u8, len: u32) -> bool {
15601663
}
15611664
```
15621665

1563-
### 3.9 Struct_ops and Kernel Module Function Pointers
1666+
### 3.10 Struct_ops and Kernel Module Function Pointers
15641667

15651668
KernelScript supports eBPF struct_ops through clean impl block syntax that allows implementing kernel interfaces using eBPF programs.
15661669

1567-
#### 3.9.1 eBPF Struct_ops with Impl Blocks
1670+
#### 3.10.1 eBPF Struct_ops with Impl Blocks
15681671

15691672
eBPF struct_ops allow implementing kernel interfaces using eBPF programs. KernelScript uses impl blocks for a clean, intuitive syntax:
15701673

@@ -1629,7 +1732,7 @@ impl my_bbr_congestion_control {
16291732
register(my_bbr_congestion_control)
16301733
```
16311734

1632-
#### 3.9.2 Simplified Struct_ops Example
1735+
#### 3.10.2 Simplified Struct_ops Example
16331736

16341737
```kernelscript
16351738
// Minimal struct_ops implementation
@@ -1669,7 +1772,7 @@ fn main() -> i32 {
16691772
}
16701773
```
16711774

1672-
#### 3.9.3 Registration Function
1775+
#### 3.10.3 Registration Function
16731776

16741777
The `register()` function is type-aware and generates the appropriate registration code:
16751778

@@ -4530,7 +4633,8 @@ kernelscript_file = { global_declaration }
45304633
45314634
global_declaration = config_declaration | map_declaration | type_declaration |
45324635
function_declaration | struct_declaration | impl_declaration |
4533-
global_variable_declaration | bindings_declaration | import_declaration
4636+
global_variable_declaration | bindings_declaration | import_declaration |
4637+
extern_declaration
45344638
45354639
(* Map declarations - global scope *)
45364640
map_declaration = [ "pin" ] [ "@flags" "(" flag_expression ")" ] "var" identifier ":" map_type "<" key_type "," value_type ">" "(" map_config ")"
@@ -4717,10 +4821,16 @@ null_literal = "null"
47174821
(* Import declarations - unified syntax for KernelScript and external languages *)
47184822
import_declaration = "import" identifier "from" string_literal
47194823
4824+
(* External kernel function declarations - for importing existing kernel kfuncs *)
4825+
extern_declaration = "extern" identifier "(" parameter_list ")" [ "->" type_annotation ]
4826+
47204827
(* Examples:
47214828
import utils from "./common/utils.ks" // KernelScript import
47224829
import ml_analysis from "./ml/threat.py" // Python import (userspace only)
47234830
4831+
extern bpf_ktime_get_ns() -> u64 // Import existing kernel kfunc
4832+
extern bpf_trace_printk(fmt: *u8, fmt_size: u32) -> i32 // Import with parameters
4833+
47244834
Import behavior is determined by file extension:
47254835
- .ks files: Import KernelScript symbols (functions, types, maps, configs)
47264836
- .py files: Import Python functions with automatic FFI bridging (userspace only)

examples/extern_kfunc_demo.ks

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// External kfunc declarations - these would typically be imported from kernel BTF
2+
extern bpf_ktime_get_ns() -> u64
3+
extern bpf_trace_printk(fmt: *u8, fmt_size: u32) -> i32
4+
extern bpf_get_current_pid_tgid() -> u64
5+
6+
// XDP program that uses external kfuncs
7+
@xdp
8+
fn packet_tracer(ctx: *xdp_md) -> xdp_action {
9+
// Get current timestamp using external kfunc
10+
var timestamp = bpf_ktime_get_ns()
11+
12+
// Get current process ID using external kfunc
13+
var pid_tgid = bpf_get_current_pid_tgid()
14+
15+
// Print debug information (this would need proper string handling in real implementation)
16+
var result = bpf_trace_printk(null, 0)
17+
18+
// Always pass packets through
19+
return 2 // XDP_PASS
20+
}
21+
22+
fn main() -> i32 {
23+
return 0
24+
}

src/ast.ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,14 @@ type import_declaration = {
343343
import_pos: position;
344344
}
345345

346+
(** Extern kfunc declaration - for importing kernel functions *)
347+
type extern_kfunc_declaration = {
348+
extern_name: string;
349+
extern_params: (string * bpf_type) list;
350+
extern_return_type: bpf_type option;
351+
extern_pos: position;
352+
}
353+
346354
(** Top-level declarations *)
347355
type declaration =
348356
| AttributedFunction of attributed_function
@@ -354,6 +362,7 @@ type declaration =
354362
| GlobalVarDecl of global_variable_declaration
355363
| ImplBlock of impl_block
356364
| ImportDecl of import_declaration
365+
| ExternKfuncDecl of extern_kfunc_declaration
357366

358367
(** Complete AST *)
359368
type ast = declaration list
@@ -440,6 +449,13 @@ let make_attributed_function attrs func pos = {
440449
tail_call_dependencies = [];
441450
}
442451

452+
let make_extern_kfunc_declaration name params return_type pos = {
453+
extern_name = name;
454+
extern_params = params;
455+
extern_return_type = return_type;
456+
extern_pos = pos;
457+
}
458+
443459
let make_type_def def = def
444460

445461
let make_enum_def name values = EnumDef (name, values)
@@ -879,6 +895,15 @@ let string_of_declaration = function
879895
import_decl.module_name
880896
import_decl.source_path
881897
source_type_str
898+
| ExternKfuncDecl extern_decl ->
899+
let params_str = String.concat ", " (List.map (fun (name, typ) ->
900+
Printf.sprintf "%s: %s" name (string_of_bpf_type typ)
901+
) extern_decl.extern_params) in
902+
let return_str = match extern_decl.extern_return_type with
903+
| Some typ -> " -> " ^ string_of_bpf_type typ
904+
| None -> ""
905+
in
906+
Printf.sprintf "extern %s(%s)%s;" extern_decl.extern_name params_str return_str
882907

883908
let string_of_ast ast =
884909
String.concat "\n\n" (List.map string_of_declaration ast)

src/lexer.mll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272

7373
let lookup_keyword = function
7474
| "fn" -> FN
75+
| "extern" -> EXTERN
7576
| "pin" -> PIN
7677
| "type" -> TYPE
7778
| "struct" -> STRUCT

src/parse.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ let validate_ast ast =
151151
| ImplStaticField (_, expr) -> validate_expr expr
152152
) impl_block.impl_items
153153
| ImportDecl _ -> true (* Import declarations are always valid once parsed *)
154+
| ExternKfuncDecl _ -> true (* Extern kfunc declarations are always valid once parsed *)
154155
in
155156

156157
List.for_all validate_declaration ast

src/parser.mly

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
%token NULL NONE
4040

4141
/* Keywords */
42-
%token FN PIN TYPE STRUCT ENUM IMPL
42+
%token FN EXTERN PIN TYPE STRUCT ENUM IMPL
4343
%token U8 U16 U32 U64 I8 I16 I32 I64 BOOL CHAR VOID STR
4444
%token IF ELSE FOR WHILE RETURN BREAK CONTINUE
4545
%token VAR CONST CONFIG LOCAL
@@ -97,6 +97,7 @@
9797
%type <Ast.map_flag> flag_item
9898

9999
%type <Ast.function_def> function_declaration
100+
%type <Ast.extern_kfunc_declaration> extern_kfunc_declaration
100101
%type <Ast.return_type_spec option> function_return_type
101102
%type <(string * Ast.bpf_type) list> parameter_list
102103
%type <string * Ast.bpf_type> parameter
@@ -170,6 +171,7 @@ declaration:
170171
| config_declaration { ConfigDecl $1 }
171172
| attributed_function_declaration { AttributedFunction $1 }
172173
| function_declaration { GlobalFunction $1 }
174+
| extern_kfunc_declaration { ExternKfuncDecl $1 }
173175
| map_declaration { MapDecl $1 }
174176
| struct_declaration { StructDecl $1 }
175177
| enum_declaration { TypeDef $1 }
@@ -211,6 +213,13 @@ function_declaration:
211213
| FN IDENTIFIER LPAREN parameter_list RPAREN function_return_type LBRACE statement_list RBRACE
212214
{ make_function $2 $4 $6 $8 (make_pos ()) }
213215

216+
/* Extern kfunc declaration: extern name(params) -> return_type; */
217+
extern_kfunc_declaration:
218+
| EXTERN IDENTIFIER LPAREN parameter_list RPAREN ARROW bpf_type
219+
{ make_extern_kfunc_declaration $2 $4 (Some $7) (make_pos ()) }
220+
| EXTERN IDENTIFIER LPAREN parameter_list RPAREN
221+
{ make_extern_kfunc_declaration $2 $4 None (make_pos ()) }
222+
214223
function_return_type:
215224
| /* empty */ { None }
216225
| ARROW bpf_type { Some (make_unnamed_return $2) }

0 commit comments

Comments
 (0)