Skip to content

Commit 5ce10be

Browse files
feat: Implement BPF struct_ops example with custom kernel module and user-space loader
1 parent ca92713 commit 5ce10be

File tree

6 files changed

+342
-122
lines changed

6 files changed

+342
-122
lines changed

src/features/struct_ops/README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# BPF struct_ops Example with Custom Kernel Module
2+
3+
This example demonstrates BPF struct_ops functionality using a custom kernel module that defines struct_ops operations triggered via a proc file write.
4+
5+
## Overview
6+
7+
struct_ops allows BPF programs to implement kernel subsystem operations dynamically. This example includes:
8+
9+
1. **Kernel Module** (`module/hello.c`) - Defines `bpf_testmod_ops` struct_ops with three callbacks
10+
2. **BPF Program** (`struct_ops.bpf.c`) - Implements the struct_ops callbacks in BPF
11+
3. **User-space Loader** (`struct_ops.c`) - Loads the BPF program and triggers callbacks via `/proc/bpf_testmod_trigger`
12+
13+
## Building and Running
14+
15+
### 1. Build the kernel module:
16+
```bash
17+
cd module
18+
make
19+
cd ..
20+
```
21+
22+
### 2. Load the kernel module:
23+
```bash
24+
sudo insmod module/hello.ko
25+
```
26+
27+
### 3. Build the BPF program:
28+
```bash
29+
make
30+
```
31+
32+
### 4. Run the example:
33+
```bash
34+
sudo ./struct_ops
35+
```
36+
37+
### 5. Check kernel logs:
38+
```bash
39+
sudo dmesg -w
40+
```
41+
42+
You should see output like:
43+
```
44+
bpf_testmod loaded with struct_ops support
45+
bpf_testmod_ops registered
46+
Calling struct_ops callbacks:
47+
BPF test_1 called!
48+
test_1() returned: 42
49+
BPF test_2 called: 10 + 20 = 30
50+
test_2(10, 20) returned: 30
51+
BPF test_3 called with buffer length 21
52+
First char: H
53+
test_3() called with buffer
54+
```
55+
56+
### 6. Clean up:
57+
```bash
58+
sudo rmmod hello
59+
make clean
60+
```
61+
62+
## How It Works
63+
64+
1. The kernel module registers a custom struct_ops type `bpf_testmod_ops`
65+
2. It creates `/proc/bpf_testmod_trigger` - writing to this file triggers the callbacks
66+
3. The BPF program implements the three callbacks: `test_1`, `test_2`, and `test_3`
67+
4. The user-space program loads the BPF program and periodically writes to the proc file
68+
5. Each write triggers all registered callbacks, demonstrating BPF struct_ops in action
69+
70+
## Troubleshooting
71+
72+
- If you get "Failed to attach struct_ops", make sure the kernel module is loaded
73+
- Check `dmesg` for any error messages from the kernel module or BPF verifier
74+
- Ensure your kernel has CONFIG_BPF_SYSCALL=y and supports struct_ops

src/features/struct_ops/module/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ obj-m += hello.o # hello.o is the target
44
KBUILD_CFLAGS += -g -O2
55

66
all:
7-
# Compile the module with BTF information
7+
# Compile the module
88
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
99

1010
clean:
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef _BPF_TESTMOD_H
2+
#define _BPF_TESTMOD_H
3+
4+
/* Shared struct_ops definition between kernel module and BPF program */
5+
struct bpf_testmod_ops {
6+
int (*test_1)(void);
7+
int (*test_2)(int a, int b);
8+
void (*test_3)(const char *buf, int len);
9+
};
10+
11+
#endif /* _BPF_TESTMOD_H */
Lines changed: 173 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,193 @@
1-
#include <linux/init.h> // Macros for module initialization
2-
#include <linux/module.h> // Core header for loading modules
3-
#include <linux/kernel.h> // Kernel logging macros
1+
#include <linux/init.h>
2+
#include <linux/module.h>
3+
#include <linux/kernel.h>
44
#include <linux/bpf.h>
55
#include <linux/btf.h>
66
#include <linux/btf_ids.h>
7+
#include <linux/proc_fs.h>
8+
#include <linux/seq_file.h>
79

8-
__bpf_kfunc int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz);
10+
/* Define our custom struct_ops operations */
11+
struct bpf_testmod_ops {
12+
int (*test_1)(void);
13+
int (*test_2)(int a, int b);
14+
void (*test_3)(const char *buf, int len);
15+
};
16+
17+
/* Global instance that BPF programs will implement */
18+
static struct bpf_testmod_ops __rcu *testmod_ops;
19+
20+
/* Proc file to trigger the struct_ops */
21+
static struct proc_dir_entry *trigger_file;
22+
23+
/* CFI stub functions - required for struct_ops */
24+
static int bpf_testmod_ops__test_1(void)
25+
{
26+
return 0;
27+
}
28+
29+
static int bpf_testmod_ops__test_2(int a, int b)
30+
{
31+
return 0;
32+
}
33+
34+
static void bpf_testmod_ops__test_3(const char *buf, int len)
35+
{
36+
}
37+
38+
/* CFI stubs structure */
39+
static struct bpf_testmod_ops __bpf_ops_bpf_testmod_ops = {
40+
.test_1 = bpf_testmod_ops__test_1,
41+
.test_2 = bpf_testmod_ops__test_2,
42+
.test_3 = bpf_testmod_ops__test_3,
43+
};
44+
45+
/* BTF and verifier callbacks */
46+
static int bpf_testmod_ops_init(struct btf *btf)
47+
{
48+
/* Initialize BTF if needed */
49+
return 0;
50+
}
51+
52+
static bool bpf_testmod_ops_is_valid_access(int off, int size,
53+
enum bpf_access_type type,
54+
const struct bpf_prog *prog,
55+
struct bpf_insn_access_aux *info)
56+
{
57+
/* Allow all accesses for now */
58+
return true;
59+
}
60+
61+
static const struct bpf_verifier_ops bpf_testmod_verifier_ops = {
62+
.is_valid_access = bpf_testmod_ops_is_valid_access,
63+
};
64+
65+
static int bpf_testmod_ops_init_member(const struct btf_type *t,
66+
const struct btf_member *member,
67+
void *kdata, const void *udata)
68+
{
69+
/* No special member initialization needed */
70+
return 0;
71+
}
72+
73+
/* Registration function */
74+
static int bpf_testmod_ops_reg(void *kdata, struct bpf_link *link)
75+
{
76+
struct bpf_testmod_ops *ops = kdata;
77+
78+
/* Only one instance at a time */
79+
if (cmpxchg(&testmod_ops, NULL, ops) != NULL)
80+
return -EEXIST;
981

10-
/* Define a kfunc function */
11-
__bpf_kfunc_start_defs();
82+
pr_info("bpf_testmod_ops registered\n");
83+
return 0;
84+
}
1285

13-
__bpf_kfunc int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz)
86+
/* Unregistration function */
87+
static void bpf_testmod_ops_unreg(void *kdata, struct bpf_link *link)
1488
{
15-
// Edge case: if substr is empty, return 0 (assuming empty string is found at the start)
16-
if (substr__sz == 0)
17-
{
18-
return 0;
19-
}
20-
// Edge case: if the substring is longer than the main string, it's impossible to find
21-
if (substr__sz > str__sz)
22-
{
23-
return -1; // Return -1 to indicate not found
24-
}
25-
26-
// Iterate through the main string, considering the size limit
27-
for (size_t i = 0; i <= str__sz - substr__sz; i++)
28-
{
29-
size_t j = 0;
30-
// Compare the substring with the current position in the string
31-
while (j < substr__sz && str[i + j] == substr[j])
32-
{
33-
j++;
34-
}
35-
// If the entire substring was found
36-
if (j == substr__sz)
37-
{
38-
return i; // Return the index of the first match
39-
}
40-
}
41-
// Return -1 if the substring is not found
42-
return -1;
89+
struct bpf_testmod_ops *ops = kdata;
90+
91+
if (cmpxchg(&testmod_ops, ops, NULL) != ops) {
92+
pr_warn("bpf_testmod_ops: unexpected unreg\n");
93+
return;
94+
}
95+
96+
pr_info("bpf_testmod_ops unregistered\n");
4397
}
4498

45-
__bpf_kfunc_end_defs();
99+
/* Struct ops definition */
100+
static struct bpf_struct_ops bpf_testmod_ops_struct_ops = {
101+
.verifier_ops = &bpf_testmod_verifier_ops,
102+
.init = bpf_testmod_ops_init,
103+
.init_member = bpf_testmod_ops_init_member,
104+
.reg = bpf_testmod_ops_reg,
105+
.unreg = bpf_testmod_ops_unreg,
106+
.cfi_stubs = &__bpf_ops_bpf_testmod_ops,
107+
.name = "bpf_testmod_ops",
108+
.owner = THIS_MODULE,
109+
};
46110

47-
BTF_KFUNCS_START(bpf_kfunc_example_ids_set)
48-
BTF_ID_FLAGS(func, bpf_strstr)
49-
BTF_KFUNCS_END(bpf_kfunc_example_ids_set)
111+
/* Proc file write handler to trigger struct_ops */
112+
static ssize_t trigger_write(struct file *file, const char __user *buf,
113+
size_t count, loff_t *pos)
114+
{
115+
struct bpf_testmod_ops *ops;
116+
char kbuf[64];
117+
int ret = 0;
118+
119+
if (count >= sizeof(kbuf))
120+
count = sizeof(kbuf) - 1;
121+
122+
if (copy_from_user(kbuf, buf, count))
123+
return -EFAULT;
124+
125+
kbuf[count] = '\0';
126+
127+
rcu_read_lock();
128+
ops = rcu_dereference(testmod_ops);
129+
if (ops) {
130+
pr_info("Calling struct_ops callbacks:\n");
131+
132+
if (ops->test_1) {
133+
ret = ops->test_1();
134+
pr_info("test_1() returned: %d\n", ret);
135+
}
136+
137+
if (ops->test_2) {
138+
ret = ops->test_2(10, 20);
139+
pr_info("test_2(10, 20) returned: %d\n", ret);
140+
}
141+
142+
if (ops->test_3) {
143+
ops->test_3(kbuf, count);
144+
pr_info("test_3() called with buffer\n");
145+
}
146+
} else {
147+
pr_info("No struct_ops registered\n");
148+
}
149+
rcu_read_unlock();
150+
151+
return count;
152+
}
50153

51-
// Register the kfunc ID set
52-
static const struct btf_kfunc_id_set bpf_kfunc_example_set = {
53-
.owner = THIS_MODULE,
54-
.set = &bpf_kfunc_example_ids_set,
154+
static const struct proc_ops trigger_proc_ops = {
155+
.proc_write = trigger_write,
55156
};
56157

57-
// Function executed when the module is loaded
58-
static int __init hello_init(void)
158+
static int __init testmod_init(void)
59159
{
60-
int ret;
61-
62-
printk(KERN_INFO "Hello, world!\n");
63-
// Register the BTF kfunc ID set
64-
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set);
65-
if (ret)
66-
{
67-
pr_err("bpf_kfunc_example: Failed to register BTF kfunc ID set\n");
68-
return ret;
69-
}
70-
printk(KERN_INFO "bpf_kfunc_example: Module loaded successfully\n");
71-
return 0; // Return 0 if successful
160+
int ret;
161+
162+
/* Register the struct_ops */
163+
ret = register_bpf_struct_ops(&bpf_testmod_ops_struct_ops, bpf_testmod_ops);
164+
if (ret) {
165+
pr_err("Failed to register struct_ops: %d\n", ret);
166+
return ret;
167+
}
168+
169+
/* Create proc file for triggering */
170+
trigger_file = proc_create("bpf_testmod_trigger", 0222, NULL, &trigger_proc_ops);
171+
if (!trigger_file) {
172+
/* Note: No unregister function available in this kernel version */
173+
return -ENOMEM;
174+
}
175+
176+
pr_info("bpf_testmod loaded with struct_ops support\n");
177+
return 0;
72178
}
73179

74-
// Function executed when the module is removed
75-
static void __exit hello_exit(void)
180+
static void __exit testmod_exit(void)
76181
{
77-
// Unregister the BTF kfunc ID set
78-
// unregister_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_kfunc_example_set);
79-
printk(KERN_INFO "Goodbye, world!\n");
182+
proc_remove(trigger_file);
183+
/* Note: struct_ops unregister happens automatically on module unload */
184+
pr_info("bpf_testmod unloaded\n");
80185
}
81186

82-
// Macros to define the module’s init and exit points
83-
module_init(hello_init);
84-
module_exit(hello_exit);
187+
module_init(testmod_init);
188+
module_exit(testmod_exit);
85189

86-
MODULE_LICENSE("GPL"); // License type (GPL)
87-
MODULE_AUTHOR("Your Name"); // Module author
88-
MODULE_DESCRIPTION("A simple module"); // Module description
89-
MODULE_VERSION("1.0"); // Module version
190+
MODULE_LICENSE("GPL");
191+
MODULE_AUTHOR("eBPF Example");
192+
MODULE_DESCRIPTION("BPF struct_ops test module");
193+
MODULE_VERSION("1.0");

0 commit comments

Comments
 (0)