英文:
Access ingress packet data on BPF_CGROUP_INET_INGRESS hook
问题
我想检查特定 cgroup 上传入数据包的数据。我发现 BPF_PROG_TYPE_CGROUP_SKB
程序类型与 BPF_CGROUP_INET_INGRESS
附加类型一起使用,可以操作 __sk_buff
结构。然而,似乎 __sk_buff
结构只暴露数据包头部,而不是数据本身?
以下是我的 BPF 程序:
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/bpf.h>
#include <sys/socket.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
char LICENSE[] SEC("license") = "GPL";
SEC("cgroup_skb/ingress")
int bpf_print_cgroup_skb(struct __sk_buff *skb) {
long err, data_len;
char skb_str[100];
long len = skb->len;
// 获取数据包的长度
if (len > 0) {
data_len = skb->data_end - skb->data;
bpf_printk("Packet length: %ld Data length: %ld\n", len, data_len);
if (len > 100) {
err = bpf_skb_load_bytes(skb, 0, &skb_str, 100);
if (err < 0) {
bpf_printk("ERR: bpf_skb_load_bytes 失败: %ld\n", err);
return SK_PASS;
}
bpf_printk("Message: %s\n", skb_str);
}
}
return SK_PASS;
}
这是如何在用户空间程序中加载程序到 cgroup 的方式:
static const char *__doc__ = "User space program to load the cgskb bpf program to register sockets\n";
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "../common/common_params.h"
#include "../common/common_user_bpf_xdp.h"
const char *cgroup_dir = "/sys/fs/cgroup/unified";
const char *cgroup_name = "<cgroup_name_here>";
int main(int argc, char **argv) {
int err, len;
int cgroup_fd;
char cgroup_filename[PATH_MAX];
const char *cgskb_file = "bpf_cgroup_skb.o";
struct bpf_object *cgskb_obj;
// 打开 cgroup fd -- 用于附加和分离操作
len = snprintf(cgroup_filename, PATH_MAX, "%s/%s", cgroup_dir, cgroup_name);
fprintf(stdout, "打开 cgroup 文件 %s\n", cgroup_filename);
cgroup_fd = open(cgroup_filename, O_RDONLY);
// 打开、加载并附加 cgskb_obj(如果尚未附加)
cgskb_obj = bpf_object__open_file(cgskb_file, NULL);
struct bpf_program *cgskb_prog = bpf_object__find_program_by_name(cgskb_obj, "bpf_print_cgroup_skb");
// 加载 cgskb 程序
err = bpf_object__load(cgskb_obj);
// 附加 cgskb 程序
// 由于 libbpf 尚不支持 cgskb,因此使用核心 BPF API。
err = bpf_prog_attach(bpf_program__fd(cgskb_prog), cgroup_fd, BPF_CGROUP_INET_INGRESS, 0);
fprintf(stdout, "成功加载 BPF 程序。\n");
return 0;
exit_cgroup:
close(cgroup_fd);
fail:
return -1;
}
是否有其他建议的 eBPF 程序类型来获取入口数据包/套接字消息数据?
英文:
I want to inspect the packet data for incoming packets on a specific cgroup. I found the BPF_PROG_TYPE_CGROUP_SKB
program type for the BPF_CGROUP_INET_INGRESS
attach type can be used to operate over the __sk_buff
struct. However, it seems that the __sk_buff
struct only exposes the packet headers, and not the data?
For reference, this is my bpf program:
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/bpf.h>
#include <sys/socket.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
char LICENSE[] SEC("license") = "GPL";
SEC("cgroup_skb/ingress")
int bpf_print_cgroup_skb(struct __sk_buff *skb) {
long err, data_len;
char skb_str[100];
long len = skb->len;
// Get length of the packet
if (len > 0) {
data_len = skb->data_end - skb->data;
bpf_printk("Packet length: %ld Data length: %ld\n", len, data_len);
if (len > 100) {
err = bpf_skb_load_bytes(skb, 0, &skb_str, 100);
if (err < 0) {
bpf_printk("ERR: bpf_skb_load_bytes failed: %ld\n", err);
return SK_PASS;
}
bpf_printk("Message: %s\n", skb_str);
}
}
return SK_PASS;
}
And this is how I load the program on a cgroup using the userspace program:
static const char *__doc__ = "User space program to load the cgskb bpf program to register sockets\n";
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "../common/common_params.h"
#include "../common/common_user_bpf_xdp.h"
const char *cgroup_dir = "/sys/fs/cgroup/unified";
const char *cgroup_name = "<cgroup_name_here>";
int main(int argc, char **argv) {
int err, len;
int cgroup_fd;
char cgroup_filename[PATH_MAX];
const char *cgskb_file = "bpf_cgroup_skb.o";
struct bpf_object *cgskb_obj;
// Open the cgroup fd -- needed for both attach and detach operations.
len = snprintf(cgroup_filename, PATH_MAX, "%s/%s", cgroup_dir, cgroup_name);
fprintf(stdout, "Opening cgroup file %s\n", cgroup_filename);
cgroup_fd = open(cgroup_filename, O_RDONLY);
// Open, load and attach cgskb_obj if not already attached
cgskb_obj = bpf_object__open_file(cgskb_file, NULL);
struct bpf_program *cgskb_prog = bpf_object__find_program_by_name(cgskb_obj, "bpf_print_cgroup_skb");
// Load the cgskb program
err = bpf_object__load(cgskb_obj);
// Attach the cgskb program
// Using core BPF API as libbpf doesn't support cgskb yet.
err = bpf_prog_attach(bpf_program__fd(cgskb_prog), cgroup_fd, BPF_CGROUP_INET_INGRESS, 0);
fprintf(stdout, "Successfully loaded BPF program.\n");
return 0;
exit_cgroup:
close(cgroup_fd);
fail:
return -1;
}
Is there any other recommend eBPF program type to get the ingress packet / socket message data.
答案1
得分: 1
以下是已翻译的内容:
"它确实看起来像是故意禁止读取头部以外的数据。添加程序类型的原始提交 https://github.com/torvalds/linux/commit/0e33661de493db325435d565a4a722120ae4cbf3 表明:
这个程序类型类似于BPF_PROG_TYPE_SOCKET_FILTER,但不允许BPF_LD_[ABS|IND]指令,并连接了bpf_skb_load_bytes()助手。
这种类型的程序将附加到cgroups以进行网络过滤和计费。
因此,通过传统方法(在直接数据访问之前)加载数据包数据在其创始时被阻止。
稍后的提交添加了直接数据访问 https://github.com/torvalds/linux/commit/b39b5f411dcfce28ff954e5d6acb2c11be3cb0ec。但它的消息说:
BPF_PROG_TYPE_CGROUP_SKB类型的BPF程序需要访问skb中的头部。此补丁启用了这些程序对skb的直接访问。
引入了两个助手函数bpf_compute_and_save_data_end()和bpf_restore_data_end()。它们在__cgroup_bpf_run_filter_skb()中使用,用于计算BPF程序的适当data_end,并在之后恢复原始数据。
而这个bpf_compute_and_save_data_end()
函数将data_end
限制为仅包括头部,而不包括主体:
cb->data_end = skb->data + skb_headlen(skb);
我仍然没有找出这个限制背后的原因。"
英文:
It indeed seems like reading data other than the headers has been purposefully disallowed. The original commit to add the program type https://github.com/torvalds/linux/commit/0e33661de493db325435d565a4a722120ae4cbf3 states:
> This program type is similar to BPF_PROG_TYPE_SOCKET_FILTER, except that
it does not allow BPF_LD_[ABS|IND] instructions and hooks up the
bpf_skb_load_bytes() helper.
>
> Programs of this type will be attached to cgroups for network filtering
and accounting.
So loading packet data via the legacy method (before direct-data access) was blocked at its inception.
A later commit adds direct-data access https://github.com/torvalds/linux/commit/b39b5f411dcfce28ff954e5d6acb2c11be3cb0ec. But its message states:
> BPF programs of BPF_PROG_TYPE_CGROUP_SKB need to access headers in the
skb. This patch enables direct access of skb for these programs.
>
> Two helper functions bpf_compute_and_save_data_end() and
bpf_restore_data_end() are introduced. There are used in
__cgroup_bpf_run_filter_skb(), to compute proper data_end for the
BPF program, and restore original data afterwards.
And this bpf_compute_and_save_data_end()
function limits the data_end
to only include the headers, never the body:
> cb->data_end = skb->data + skb_headlen(skb);
I have yet to find out what the reason behind this limitation is.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论