/* uprobe_nginx.c — Tracepoints syscall pour capturer le trafic HTTP depuis nginx * * Cette version utilise les tracepoints kernel syscalls/sys_enter_recvfrom et * syscalls/sys_exit_recvfrom pour capturer les appels système recvfrom(). * Le filtrage par PID nginx permet de capturer uniquement le trafic HTTP du serveur. * * ============================================================================ */ #include "vmlinux.h" #include #include #include "bpf_types.h" /* Taille maximale d'une capture recvfrom */ #define MAX_RECVFROM_SIZE 4096 /* Structure pour stocker les arguments recvfrom entre enter et exit */ struct recvfrom_args { __s32 sockfd; __u64 buf_ptr; __u64 len; __s64 flags; } __attribute__((packed)); /* ============================================================================ * tracepoint_sys_enter_recvfrom — Entrée du syscall recvfrom * * Sauvegarde les arguments si le PID correspond à nginx. * Signature: ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, * struct sockaddr *src_addr, socklen_t *addrlen); * ============================================================================ */ SEC("tp/syscalls/sys_enter_recvfrom") int tp_sys_enter_recvfrom(struct trace_event_raw_sys_enter *ctx) { __u64 pid_tgid = bpf_get_current_pid_tgid(); __u32 pid = pid_tgid >> 32; /* Vérifier si le PID est dans la liste des PIDs nginx */ __u8 *is_nginx = bpf_map_lookup_elem(&nginx_pid_map, &pid); if (!is_nginx) return 0; /* Pas nginx, ignorer */ /* Sauvegarder les arguments pour le exit */ struct recvfrom_args args = {}; args.sockfd = ctx->args[0]; /* int sockfd */ args.buf_ptr = ctx->args[1]; /* void *buf */ args.len = ctx->args[2]; /* size_t len */ args.flags = ctx->args[3]; /* int flags */ /* ctx->args[4] et [5] sont src_addr et addrlen, ignorés */ /* Stocker dans une map hash pour récupération dans le exit */ bpf_map_update_elem(&nginx_read_args_map, &pid_tgid, &args, BPF_ANY); return 0; } /* ============================================================================ * kretprobe_sys_exit_recvfrom — Sortie du syscall recvfrom * * Capture les données lues si le PID correspond à nginx. * * NOTE: Utilisation de kretprobe sur __x64_sys_recvfrom pour contourner * le bug "permission denied" des tracepoints sur certains kernels (Rocky Linux 9). * Les kretprobes ciblent directement la fonction kernel, évitant les restrictions. * ============================================================================ */ SEC("kretprobe/__x64_sys_recvfrom") int tp_sys_exit_recvfrom(struct pt_regs *ctx) { __u64 pid_tgid = bpf_get_current_pid_tgid(); __u32 pid = pid_tgid >> 32; /* Vérifier si le PID est dans la liste des PIDs nginx */ __u8 *is_nginx = bpf_map_lookup_elem(&nginx_pid_map, &pid); if (!is_nginx) return 0; /* Pas nginx, ignorer */ /* Récupérer les arguments sauvegardés */ struct recvfrom_args *args = bpf_map_lookup_elem(&nginx_read_args_map, &pid_tgid); if (!args) return 0; /* Vérifier que la lecture a réussi (valeur de retour > 0) * Pour kretprobe, la valeur de retour est dans PT_REGS_RC */ long retval = PT_REGS_RC(ctx); if (retval <= 0) { bpf_map_delete_elem(&nginx_read_args_map, &pid_tgid); return 0; } /* Limiter la capture */ __u32 data_len = retval; if (data_len > MAX_RECVFROM_SIZE) data_len = MAX_RECVFROM_SIZE; /* Buffer PERCPU */ __u32 zero = 0; struct nginx_http_event *evt = bpf_map_lookup_elem(&__nginx_buf, &zero); if (!evt) { bpf_map_delete_elem(&nginx_read_args_map, &pid_tgid); return 0; } /* Initialiser l'événement */ evt->pid_tgid = pid_tgid; evt->fd = args->sockfd; evt->src_ip = 0; /* Sera rempli via corrélation TC si disponible */ evt->src_port = 0; evt->timestamp_ns = bpf_ktime_get_ns(); evt->method_len = 0; evt->uri_len = 0; evt->query_len = 0; evt->body_len = 0; evt->data_len = 0; /* Copier les données brutes depuis le buffer */ if (data_len > 0) { __u32 copy_len = data_len; if (copy_len > sizeof(evt->data)) copy_len = sizeof(evt->data); bpf_probe_read_user(evt->data, copy_len, (void *)args->buf_ptr); evt->data_len = copy_len; } /* Émettre l'événement brut vers userspace */ bpf_perf_event_output(ctx, &pb_ginx_http, BPF_F_CURRENT_CPU, evt, sizeof(*evt)); bpf_map_delete_elem(&nginx_read_args_map, &pid_tgid); return 0; } char LICENSE[] SEC("license") = "GPL";