mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-05-05 16:44:44 +08:00
feat: Implement BPF struct_ops example with custom kernel module and user-space loader
This commit is contained in:
74
src/features/struct_ops/README.md
Normal file
74
src/features/struct_ops/README.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# BPF struct_ops Example with Custom Kernel Module
|
||||||
|
|
||||||
|
This example demonstrates BPF struct_ops functionality using a custom kernel module that defines struct_ops operations triggered via a proc file write.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
struct_ops allows BPF programs to implement kernel subsystem operations dynamically. This example includes:
|
||||||
|
|
||||||
|
1. **Kernel Module** (`module/hello.c`) - Defines `bpf_testmod_ops` struct_ops with three callbacks
|
||||||
|
2. **BPF Program** (`struct_ops.bpf.c`) - Implements the struct_ops callbacks in BPF
|
||||||
|
3. **User-space Loader** (`struct_ops.c`) - Loads the BPF program and triggers callbacks via `/proc/bpf_testmod_trigger`
|
||||||
|
|
||||||
|
## Building and Running
|
||||||
|
|
||||||
|
### 1. Build the kernel module:
|
||||||
|
```bash
|
||||||
|
cd module
|
||||||
|
make
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Load the kernel module:
|
||||||
|
```bash
|
||||||
|
sudo insmod module/hello.ko
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Build the BPF program:
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Run the example:
|
||||||
|
```bash
|
||||||
|
sudo ./struct_ops
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Check kernel logs:
|
||||||
|
```bash
|
||||||
|
sudo dmesg -w
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see output like:
|
||||||
|
```
|
||||||
|
bpf_testmod loaded with struct_ops support
|
||||||
|
bpf_testmod_ops registered
|
||||||
|
Calling struct_ops callbacks:
|
||||||
|
BPF test_1 called!
|
||||||
|
test_1() returned: 42
|
||||||
|
BPF test_2 called: 10 + 20 = 30
|
||||||
|
test_2(10, 20) returned: 30
|
||||||
|
BPF test_3 called with buffer length 21
|
||||||
|
First char: H
|
||||||
|
test_3() called with buffer
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Clean up:
|
||||||
|
```bash
|
||||||
|
sudo rmmod hello
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. The kernel module registers a custom struct_ops type `bpf_testmod_ops`
|
||||||
|
2. It creates `/proc/bpf_testmod_trigger` - writing to this file triggers the callbacks
|
||||||
|
3. The BPF program implements the three callbacks: `test_1`, `test_2`, and `test_3`
|
||||||
|
4. The user-space program loads the BPF program and periodically writes to the proc file
|
||||||
|
5. Each write triggers all registered callbacks, demonstrating BPF struct_ops in action
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- If you get "Failed to attach struct_ops", make sure the kernel module is loaded
|
||||||
|
- Check `dmesg` for any error messages from the kernel module or BPF verifier
|
||||||
|
- Ensure your kernel has CONFIG_BPF_SYSCALL=y and supports struct_ops
|
||||||
@@ -4,7 +4,7 @@ obj-m += hello.o # hello.o is the target
|
|||||||
KBUILD_CFLAGS += -g -O2
|
KBUILD_CFLAGS += -g -O2
|
||||||
|
|
||||||
all:
|
all:
|
||||||
# Compile the module with BTF information
|
# Compile the module
|
||||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
11
src/features/struct_ops/module/bpf_testmod.h
Normal file
11
src/features/struct_ops/module/bpf_testmod.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _BPF_TESTMOD_H
|
||||||
|
#define _BPF_TESTMOD_H
|
||||||
|
|
||||||
|
/* Shared struct_ops definition between kernel module and BPF program */
|
||||||
|
struct bpf_testmod_ops {
|
||||||
|
int (*test_1)(void);
|
||||||
|
int (*test_2)(int a, int b);
|
||||||
|
void (*test_3)(const char *buf, int len);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _BPF_TESTMOD_H */
|
||||||
@@ -1,89 +1,193 @@
|
|||||||
#include <linux/init.h> // Macros for module initialization
|
#include <linux/init.h>
|
||||||
#include <linux/module.h> // Core header for loading modules
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h> // Kernel logging macros
|
#include <linux/kernel.h>
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include <linux/btf.h>
|
#include <linux/btf.h>
|
||||||
#include <linux/btf_ids.h>
|
#include <linux/btf_ids.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
|
||||||
__bpf_kfunc int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz);
|
/* Define our custom struct_ops operations */
|
||||||
|
struct bpf_testmod_ops {
|
||||||
/* Define a kfunc function */
|
int (*test_1)(void);
|
||||||
__bpf_kfunc_start_defs();
|
int (*test_2)(int a, int b);
|
||||||
|
void (*test_3)(const char *buf, int len);
|
||||||
__bpf_kfunc int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz)
|
|
||||||
{
|
|
||||||
// Edge case: if substr is empty, return 0 (assuming empty string is found at the start)
|
|
||||||
if (substr__sz == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Edge case: if the substring is longer than the main string, it's impossible to find
|
|
||||||
if (substr__sz > str__sz)
|
|
||||||
{
|
|
||||||
return -1; // Return -1 to indicate not found
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through the main string, considering the size limit
|
|
||||||
for (size_t i = 0; i <= str__sz - substr__sz; i++)
|
|
||||||
{
|
|
||||||
size_t j = 0;
|
|
||||||
// Compare the substring with the current position in the string
|
|
||||||
while (j < substr__sz && str[i + j] == substr[j])
|
|
||||||
{
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
// If the entire substring was found
|
|
||||||
if (j == substr__sz)
|
|
||||||
{
|
|
||||||
return i; // Return the index of the first match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return -1 if the substring is not found
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
__bpf_kfunc_end_defs();
|
|
||||||
|
|
||||||
BTF_KFUNCS_START(bpf_kfunc_example_ids_set)
|
|
||||||
BTF_ID_FLAGS(func, bpf_strstr)
|
|
||||||
BTF_KFUNCS_END(bpf_kfunc_example_ids_set)
|
|
||||||
|
|
||||||
// Register the kfunc ID set
|
|
||||||
static const struct btf_kfunc_id_set bpf_kfunc_example_set = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.set = &bpf_kfunc_example_ids_set,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function executed when the module is loaded
|
/* Global instance that BPF programs will implement */
|
||||||
static int __init hello_init(void)
|
static struct bpf_testmod_ops __rcu *testmod_ops;
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
printk(KERN_INFO "Hello, world!\n");
|
/* Proc file to trigger the struct_ops */
|
||||||
// Register the BTF kfunc ID set
|
static struct proc_dir_entry *trigger_file;
|
||||||
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set);
|
|
||||||
if (ret)
|
/* CFI stub functions - required for struct_ops */
|
||||||
{
|
static int bpf_testmod_ops__test_1(void)
|
||||||
pr_err("bpf_kfunc_example: Failed to register BTF kfunc ID set\n");
|
{
|
||||||
return ret;
|
return 0;
|
||||||
}
|
|
||||||
printk(KERN_INFO "bpf_kfunc_example: Module loaded successfully\n");
|
|
||||||
return 0; // Return 0 if successful
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function executed when the module is removed
|
static int bpf_testmod_ops__test_2(int a, int b)
|
||||||
static void __exit hello_exit(void)
|
|
||||||
{
|
{
|
||||||
// Unregister the BTF kfunc ID set
|
return 0;
|
||||||
// unregister_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_kfunc_example_set);
|
|
||||||
printk(KERN_INFO "Goodbye, world!\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macros to define the module’s init and exit points
|
static void bpf_testmod_ops__test_3(const char *buf, int len)
|
||||||
module_init(hello_init);
|
{
|
||||||
module_exit(hello_exit);
|
}
|
||||||
|
|
||||||
MODULE_LICENSE("GPL"); // License type (GPL)
|
/* CFI stubs structure */
|
||||||
MODULE_AUTHOR("Your Name"); // Module author
|
static struct bpf_testmod_ops __bpf_ops_bpf_testmod_ops = {
|
||||||
MODULE_DESCRIPTION("A simple module"); // Module description
|
.test_1 = bpf_testmod_ops__test_1,
|
||||||
MODULE_VERSION("1.0"); // Module version
|
.test_2 = bpf_testmod_ops__test_2,
|
||||||
|
.test_3 = bpf_testmod_ops__test_3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* BTF and verifier callbacks */
|
||||||
|
static int bpf_testmod_ops_init(struct btf *btf)
|
||||||
|
{
|
||||||
|
/* Initialize BTF if needed */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bpf_testmod_ops_is_valid_access(int off, int size,
|
||||||
|
enum bpf_access_type type,
|
||||||
|
const struct bpf_prog *prog,
|
||||||
|
struct bpf_insn_access_aux *info)
|
||||||
|
{
|
||||||
|
/* Allow all accesses for now */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct bpf_verifier_ops bpf_testmod_verifier_ops = {
|
||||||
|
.is_valid_access = bpf_testmod_ops_is_valid_access,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bpf_testmod_ops_init_member(const struct btf_type *t,
|
||||||
|
const struct btf_member *member,
|
||||||
|
void *kdata, const void *udata)
|
||||||
|
{
|
||||||
|
/* No special member initialization needed */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Registration function */
|
||||||
|
static int bpf_testmod_ops_reg(void *kdata, struct bpf_link *link)
|
||||||
|
{
|
||||||
|
struct bpf_testmod_ops *ops = kdata;
|
||||||
|
|
||||||
|
/* Only one instance at a time */
|
||||||
|
if (cmpxchg(&testmod_ops, NULL, ops) != NULL)
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
pr_info("bpf_testmod_ops registered\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unregistration function */
|
||||||
|
static void bpf_testmod_ops_unreg(void *kdata, struct bpf_link *link)
|
||||||
|
{
|
||||||
|
struct bpf_testmod_ops *ops = kdata;
|
||||||
|
|
||||||
|
if (cmpxchg(&testmod_ops, ops, NULL) != ops) {
|
||||||
|
pr_warn("bpf_testmod_ops: unexpected unreg\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("bpf_testmod_ops unregistered\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Struct ops definition */
|
||||||
|
static struct bpf_struct_ops bpf_testmod_ops_struct_ops = {
|
||||||
|
.verifier_ops = &bpf_testmod_verifier_ops,
|
||||||
|
.init = bpf_testmod_ops_init,
|
||||||
|
.init_member = bpf_testmod_ops_init_member,
|
||||||
|
.reg = bpf_testmod_ops_reg,
|
||||||
|
.unreg = bpf_testmod_ops_unreg,
|
||||||
|
.cfi_stubs = &__bpf_ops_bpf_testmod_ops,
|
||||||
|
.name = "bpf_testmod_ops",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Proc file write handler to trigger struct_ops */
|
||||||
|
static ssize_t trigger_write(struct file *file, const char __user *buf,
|
||||||
|
size_t count, loff_t *pos)
|
||||||
|
{
|
||||||
|
struct bpf_testmod_ops *ops;
|
||||||
|
char kbuf[64];
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (count >= sizeof(kbuf))
|
||||||
|
count = sizeof(kbuf) - 1;
|
||||||
|
|
||||||
|
if (copy_from_user(kbuf, buf, count))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
kbuf[count] = '\0';
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
ops = rcu_dereference(testmod_ops);
|
||||||
|
if (ops) {
|
||||||
|
pr_info("Calling struct_ops callbacks:\n");
|
||||||
|
|
||||||
|
if (ops->test_1) {
|
||||||
|
ret = ops->test_1();
|
||||||
|
pr_info("test_1() returned: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->test_2) {
|
||||||
|
ret = ops->test_2(10, 20);
|
||||||
|
pr_info("test_2(10, 20) returned: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->test_3) {
|
||||||
|
ops->test_3(kbuf, count);
|
||||||
|
pr_info("test_3() called with buffer\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_info("No struct_ops registered\n");
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct proc_ops trigger_proc_ops = {
|
||||||
|
.proc_write = trigger_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init testmod_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Register the struct_ops */
|
||||||
|
ret = register_bpf_struct_ops(&bpf_testmod_ops_struct_ops, bpf_testmod_ops);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Failed to register struct_ops: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create proc file for triggering */
|
||||||
|
trigger_file = proc_create("bpf_testmod_trigger", 0222, NULL, &trigger_proc_ops);
|
||||||
|
if (!trigger_file) {
|
||||||
|
/* Note: No unregister function available in this kernel version */
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("bpf_testmod loaded with struct_ops support\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit testmod_exit(void)
|
||||||
|
{
|
||||||
|
proc_remove(trigger_file);
|
||||||
|
/* Note: struct_ops unregister happens automatically on module unload */
|
||||||
|
pr_info("bpf_testmod unloaded\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(testmod_init);
|
||||||
|
module_exit(testmod_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("eBPF Example");
|
||||||
|
MODULE_DESCRIPTION("BPF struct_ops test module");
|
||||||
|
MODULE_VERSION("1.0");
|
||||||
@@ -1,28 +1,40 @@
|
|||||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#define BPF_NO_GLOBAL_DATA
|
#include <vmlinux.h>
|
||||||
#include <linux/bpf.h>
|
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
|
#include "module/bpf_testmod.h"
|
||||||
|
|
||||||
typedef unsigned int u32;
|
char _license[] SEC("license") = "GPL";
|
||||||
typedef unsigned long long u64;
|
|
||||||
typedef int pid_t;
|
|
||||||
|
|
||||||
extern int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz) __ksym;
|
/* Implement the struct_ops callbacks */
|
||||||
|
SEC("struct_ops/test_1")
|
||||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
int BPF_PROG(bpf_testmod_test_1)
|
||||||
|
|
||||||
SEC("kprobe/do_unlinkat")
|
|
||||||
int handle_kprobe(void *ctx)
|
|
||||||
{
|
{
|
||||||
pid_t pid = bpf_get_current_pid_tgid() >> 32;
|
bpf_printk("BPF test_1 called!\n");
|
||||||
char str[] = "Hello, world!";
|
return 42;
|
||||||
char substr[] = "wor";
|
|
||||||
u32 result = bpf_strstr(str, sizeof(str) - 1, substr, sizeof(substr) - 1);
|
|
||||||
if (result != -1)
|
|
||||||
{
|
|
||||||
bpf_printk("'%s' found in '%s' at index %d\n", substr, str, result);
|
|
||||||
}
|
|
||||||
bpf_printk("Hello, world! (pid: %d) bpf_strstr %d\n", pid, result);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SEC("struct_ops/test_2")
|
||||||
|
int BPF_PROG(bpf_testmod_test_2, int a, int b)
|
||||||
|
{
|
||||||
|
int result = a + b;
|
||||||
|
bpf_printk("BPF test_2 called: %d + %d = %d\n", a, b, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEC("struct_ops/test_3")
|
||||||
|
void BPF_PROG(bpf_testmod_test_3, const char *buf, int len)
|
||||||
|
{
|
||||||
|
bpf_printk("BPF test_3 called with buffer length %d\n", len);
|
||||||
|
if (len > 0) {
|
||||||
|
bpf_printk("First char: %c\n", buf[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Define the struct_ops map */
|
||||||
|
SEC(".struct_ops")
|
||||||
|
struct bpf_testmod_ops testmod_ops = {
|
||||||
|
.test_1 = (void *)bpf_testmod_test_1,
|
||||||
|
.test_2 = (void *)bpf_testmod_test_2,
|
||||||
|
.test_3 = (void *)bpf_testmod_test_3,
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,75 +2,94 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <bpf/bpf.h>
|
||||||
|
#include <bpf/libbpf.h>
|
||||||
|
|
||||||
#include "struct_ops.skel.h" // Include the generated skeleton header
|
#include "struct_ops.skel.h"
|
||||||
|
|
||||||
static volatile bool exiting = false;
|
static volatile bool exiting = false;
|
||||||
|
|
||||||
// Signal handler for graceful termination
|
|
||||||
void handle_signal(int sig) {
|
void handle_signal(int sig) {
|
||||||
exiting = true;
|
exiting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int trigger_struct_ops(const char *message) {
|
||||||
|
int fd, ret;
|
||||||
|
|
||||||
|
fd = open("/proc/bpf_testmod_trigger", O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("open /proc/bpf_testmod_trigger");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = write(fd, message, strlen(message));
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("write");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
struct struct_ops_bpf *skel;
|
struct struct_ops_bpf *skel;
|
||||||
|
struct bpf_link *link;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
// Handle SIGINT and SIGTERM for graceful shutdown
|
|
||||||
signal(SIGINT, handle_signal);
|
signal(SIGINT, handle_signal);
|
||||||
|
signal(SIGTERM, handle_signal);
|
||||||
|
|
||||||
// Open the BPF application
|
/* Open BPF application */
|
||||||
skel = struct_ops_bpf__open();
|
skel = struct_ops_bpf__open();
|
||||||
if (!skel) {
|
if (!skel) {
|
||||||
fprintf(stderr, "Failed to open BPF skeleton\n");
|
fprintf(stderr, "Failed to open BPF skeleton\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load & verify the BPF program
|
/* Load BPF programs */
|
||||||
err = struct_ops_bpf__load(skel);
|
err = struct_ops_bpf__load(skel);
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "Failed to load and verify BPF skeleton: %d\n", err);
|
fprintf(stderr, "Failed to load BPF skeleton: %d\n", err);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the BPF program (e.g., attach kprobe)
|
/* Register struct_ops */
|
||||||
err = struct_ops_bpf__attach(skel);
|
link = bpf_map__attach_struct_ops(skel->maps.testmod_ops);
|
||||||
if (err) {
|
if (!link) {
|
||||||
fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err);
|
fprintf(stderr, "Failed to attach struct_ops\n");
|
||||||
|
err = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("BPF program loaded and attached successfully. Press Ctrl-C to exit.\n");
|
printf("Successfully loaded and attached BPF struct_ops!\n");
|
||||||
|
printf("Triggering struct_ops callbacks...\n");
|
||||||
// Optionally, read the trace_pipe to see bpf_printk outputs
|
|
||||||
FILE *trace_pipe = fopen("/sys/kernel/debug/tracing/trace_pipe", "r");
|
/* Trigger the struct_ops by writing to proc file */
|
||||||
if (!trace_pipe) {
|
if (trigger_struct_ops("Hello from userspace!") < 0) {
|
||||||
perror("fopen trace_pipe");
|
printf("Failed to trigger struct_ops - is the kernel module loaded?\n");
|
||||||
// Continue without reading trace_pipe
|
printf("Load it with: sudo insmod module/hello.ko\n");
|
||||||
|
} else {
|
||||||
|
printf("Triggered struct_ops successfully! Check dmesg for output.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("\nPress Ctrl-C to exit...\n");
|
||||||
|
|
||||||
// Main loop
|
/* Main loop - trigger periodically */
|
||||||
while (!exiting) {
|
while (!exiting) {
|
||||||
if (trace_pipe) {
|
sleep(2);
|
||||||
char buffer[256];
|
if (!exiting && trigger_struct_ops("Periodic trigger") == 0) {
|
||||||
if (fgets(buffer, sizeof(buffer), trace_pipe)) {
|
printf("Triggered struct_ops again...\n");
|
||||||
printf("%s", buffer);
|
|
||||||
} else {
|
|
||||||
if (errno == EINTR)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If trace_pipe is not available, just sleep
|
|
||||||
sleep(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trace_pipe)
|
printf("\nDetaching struct_ops...\n");
|
||||||
fclose(trace_pipe);
|
bpf_link__destroy(link);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
// Clean up and destroy the BPF program
|
|
||||||
struct_ops_bpf__destroy(skel);
|
struct_ops_bpf__destroy(skel);
|
||||||
return err < 0 ? -err : 0;
|
return err < 0 ? -err : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user