在BPF_PROG_TYPE_SK_LOOKUP中获取连接所属进程的PID
如何在eBPF sk_msg上下文获取客户端连接的进程PID
你遇到的核心问题有两个:
bpf_get_current_pid_tgid()在sk_msg钩子中拿到的是当前处理消息的进程(比如代理进程)的PID,而非发起该连接的客户端进程PID;- 即便该函数可用,也无法满足你过滤客户端PID的需求。
正确的思路是在连接建立阶段记录客户端PID与socket的关联,再在sk_msg阶段通过socket标识查询对应的PID。具体实现步骤如下:
1. 定义eBPF映射存储PID与socket的关联
首先创建一个哈希映射,用socket的cookie作为键,客户端PID作为值:
struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, u64); // socket cookie __type(value, u32); // 客户端PID(内核态tgid,对应用户态PID) } sock_pid_map SEC(".maps");
2. 在连接建立钩子中记录关联
使用sockops钩子或者tracepoint钩子捕获连接建立事件,记录socket的cookie和对应的PID:
方法一:使用sockops钩子(内核4.17+支持)
SEC("sockops") int sockops_handler(struct bpf_sock_ops *ctx) { u32 op = ctx->op; // 捕获TCP连接建立完成事件 if (op == BPF_SOCK_OPS_TCP_CONNECT_CB) { u64 sock_cookie = bpf_get_socket_cookie(ctx->sk); u32 pid = bpf_get_current_pid_tgid() >> 32; bpf_map_update_elem(&sock_pid_map, &sock_cookie, &pid, BPF_ANY); } // 连接关闭时清理映射,避免内存泄漏 else if (op == BPF_SOCK_OPS_TCP_CLOSE_CB) { u64 sock_cookie = bpf_get_socket_cookie(ctx->sk); bpf_map_delete_elem(&sock_pid_map, &sock_cookie); } return 0; }
方法二:使用tracepoint钩子(更直观,内核4.15+支持)
SEC("tracepoint/syscalls/sys_enter_connect") int trace_connect(struct trace_event_raw_sys_enter *ctx) { struct socket *sock = (struct socket *)ctx->args[0]; if (!sock) return 0; u64 sock_cookie = bpf_get_socket_cookie(sock->sk); u32 pid = bpf_get_current_pid_tgid() >> 32; bpf_map_update_elem(&sock_pid_map, &sock_cookie, &pid, BPF_ANY); return 0; } // 可选:添加sys_exit_close钩子清理已关闭连接的映射条目
3. 在sk_msg钩子中查询PID并过滤
回到你的echo_dispatch程序,获取当前socket的cookie,从映射中查询客户端PID,再执行过滤逻辑:
SEC("sk_msg") int echo_dispatch(struct sk_msg_md *msg) { struct sock *sk = msg->sk; if (!sk) return SK_PASS; u64 sock_cookie = bpf_get_socket_cookie(sk); u32 *client_pid = bpf_map_lookup_elem(&sock_pid_map, &sock_cookie); if (!client_pid) return SK_PASS; // 替换为你需要过滤的目标PID if (*client_pid == YOUR_TARGET_PID) { // 执行代理逻辑,比如转发或丢弃 return SK_PASS; // 或SK_DROP,根据需求调整 } return SK_PASS; }
注意事项
- 内核版本兼容性:确保内核支持所需的eBPF辅助函数(
bpf_get_socket_cookie需4.15+,sockops钩子需4.17+); - 映射清理:必须在连接关闭时清理映射条目,避免内存泄漏;
- 权限要求:加载eBPF程序时需要
CAP_BPF、CAP_PERFMON等权限; - 连接复用场景:如果同一个socket被多进程复用,会记录最后操作该socket的进程PID,需根据业务场景调整逻辑。
内容的提问来源于stack exchange,提问作者Shubham Jain
相关产品推荐
相关产品推荐

