feat(ebpf): add Apache httpd HTTP capture via kretprobe recvfrom
- Add uprobe_apache.c with kretprobe on __x64_sys_recvfrom for Apache HTTP capture - Update loader.go to support unified "servers" configuration instead of separate nginx_bin_path/apache_enabled - Add consumeApacheHTTPEvents() function to process Apache HTTP events - Update bpf_types.h to add Apache-specific BPF maps and structs - Fix perf event array value_size for pb_apache_http (must be sizeof(__u32) not struct size) - Add NGINX_APACHE_GUIDE.md documentation for HTTP capture from both servers Validation results: - nginx HTTP capture: ✅ Working (57 headers captured, no truncation) - Apache HTTP capture: ⚠️ Under investigation (kretprobe not triggering on CentOS 8 kernel 4.18) Configuration: - JA4EBPF_UPROBES_ENABLED=true - JA4EBPF_UPROBES_SERVERS=nginx,apache (or "both") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
/* uprobe_apache.c — Tracepoints syscall pour capturer le trafic HTTP depuis Apache httpd
|
||||
*
|
||||
* Cette version utilise les tracepoints kernel syscalls/sys_enter_read et
|
||||
* kretprobe sur __x64_sys_read pour capturer les appels système read() du serveur Apache.
|
||||
* Cette version utilise kretprobe sur __x64_sys_recvfrom pour capturer les appels
|
||||
* système recvfrom() du serveur Apache httpd (identique à nginx).
|
||||
* Le filtrage par PID Apache permet de capturer uniquement le trafic HTTP du serveur.
|
||||
*
|
||||
* ============================================================================
|
||||
@ -12,18 +12,19 @@
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_types.h"
|
||||
|
||||
/* Taille maximale d'une capture read() */
|
||||
#define MAX_READ_SIZE 4096
|
||||
/* Taille maximale d'une capture recvfrom() */
|
||||
#define MAX_RECV_SIZE 4096
|
||||
|
||||
/* ============================================================================
|
||||
* tracepoint_sys_enter_read — Entrée du syscall read
|
||||
* kretprobe_sys_exit_recvfrom — Sortie du syscall recvfrom
|
||||
*
|
||||
* Sauvegarde les arguments si le PID correspond à Apache.
|
||||
* Signature: ssize_t read(int fd, void *buf, size_t count);
|
||||
* Capture les données reçues et les envoie vers pb_apache_http.
|
||||
* 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_read")
|
||||
int tp_sys_enter_read(struct trace_event_raw_sys_enter *ctx)
|
||||
SEC("kretprobe/__x64_sys_recvfrom")
|
||||
int kretprobe_sys_exit_recvfrom(struct pt_regs *ctx)
|
||||
{
|
||||
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
||||
__u32 pid = pid_tgid >> 32;
|
||||
@ -35,80 +36,49 @@ int tp_sys_enter_read(struct trace_event_raw_sys_enter *ctx)
|
||||
return 0; /* Pas un PID Apache, ignore */
|
||||
}
|
||||
|
||||
/* Sauvegarder les arguments pour l'exit tracepoint */
|
||||
struct read_args args = {};
|
||||
args.fd = (__s32)ctx->args[0]; /* fd */
|
||||
args.buf_ptr = (__u64)ctx->args[1]; /* buf */
|
||||
args.count = (__u64)ctx->args[2]; /* count */
|
||||
|
||||
bpf_map_update_elem(&apache_read_args_map, &pid_tgid, &args, BPF_ANY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* kretprobe_sys_exit_read — Sortie du syscall read
|
||||
*
|
||||
* Capture les données lues et les envoie vers pb_apache_http.
|
||||
* Utilise kretprobe pour contourner les limitations de tracepoint exit.
|
||||
* ============================================================================
|
||||
*/
|
||||
SEC("kretprobe/__x64_sys_read")
|
||||
int kretprobe_sys_exit_read(struct pt_regs *ctx)
|
||||
{
|
||||
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
||||
|
||||
/* Récupérer les arguments sauvegardés */
|
||||
struct read_args *args = bpf_map_lookup_elem(&apache_read_args_map, &pid_tgid);
|
||||
if (!args) {
|
||||
return 0; /* Pas d'arguments correspondants */
|
||||
}
|
||||
|
||||
/* Obtenir la valeur de retour (nombre d'octets lus) */
|
||||
/* Obtenir la valeur de retour (nombre d'octets reçus) */
|
||||
long retval = PT_REGS_RC(ctx);
|
||||
if (retval <= 0 || retval > MAX_READ_SIZE) {
|
||||
/* Erreur, EOF, ou trop de données - nettoyer et sortir */
|
||||
bpf_map_delete_elem(&apache_read_args_map, &pid_tgid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Taille à copier (minimum entre retval et la taille disponible) */
|
||||
__u64 copy_size = retval;
|
||||
if (copy_size > args->count) {
|
||||
copy_size = args->count;
|
||||
if (retval <= 0 || retval > MAX_RECV_SIZE) {
|
||||
return 0; /* Erreur, EOF, ou trop de données */
|
||||
}
|
||||
|
||||
/* Préparer l'événement Apache HTTP */
|
||||
struct apache_http_event *e = bpf_map_lookup_elem(&__apache_buf, &pid_tgid);
|
||||
if (!e) {
|
||||
bpf_map_delete_elem(&apache_read_args_map, &pid_tgid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialiser l'événement */
|
||||
__builtin_memset(e, 0, sizeof(*e));
|
||||
e->pid_tgid = pid_tgid;
|
||||
e->fd = args->fd;
|
||||
e->fd = 0; /* sockfd n'est pas disponible en kretprobe sans arguments sauvegardés */
|
||||
e->src_ip = 0; /* Sera rempli via corrélation TC si disponible */
|
||||
e->src_port = 0;
|
||||
e->timestamp_ns = bpf_ktime_get_ns();
|
||||
e->method_len = 0;
|
||||
e->uri_len = 0;
|
||||
e->query_len = 0;
|
||||
e->body_len = 0;
|
||||
e->data_len = 0;
|
||||
|
||||
/* Récupérer les infos de connexion depuis fd_conn_map */
|
||||
struct ssl_conn_info *conn_info = bpf_map_lookup_elem(&fd_conn_map, &args->fd);
|
||||
if (conn_info) {
|
||||
e->src_ip = conn_info->src_ip;
|
||||
e->src_port = conn_info->src_port;
|
||||
/* Copier les données brutes depuis la stack (recvfrom buffer)
|
||||
* Note: Comme nous n'avons pas sauvegardé les arguments à l'entrée,
|
||||
* nous ne pouvons pas accéder au buffer utilisateur directement.
|
||||
* Pour Apache, nous utilisons une approche simplifiée qui capture
|
||||
* les données depuis le contexte BPF disponible.
|
||||
*/
|
||||
if (retval > 0 && retval < sizeof(e->data)) {
|
||||
/* Lire depuis le premier argument de la stack (buf pointer)
|
||||
* Sur x86_64, les arguments sont dans: RDI=sockfd, RSI=buf, RDX=len, R10=flags
|
||||
*/
|
||||
__u64 buf_ptr = PT_REGS_PARM2(ctx);
|
||||
__u64 bytes_read = bpf_probe_read_user_str(e->data, sizeof(e->data), (void *)buf_ptr);
|
||||
if (bytes_read > 0) {
|
||||
e->data_len = bytes_read;
|
||||
/* Envoyer vers l'espace utilisateur via perf buffer */
|
||||
bpf_perf_event_output(ctx, &pb_apache_http, BPF_F_CURRENT_CPU, e, sizeof(*e));
|
||||
}
|
||||
}
|
||||
|
||||
/* Copier les données HTTP depuis l'espace utilisateur */
|
||||
__u64 bytes_read = bpf_probe_read_user_str(e->data, sizeof(e->data), (void *)args->buf_ptr);
|
||||
if (bytes_read > 0) {
|
||||
e->data_len = bytes_read;
|
||||
/* Envoyer vers l'espace utilisateur via perf buffer */
|
||||
bpf_perf_event_output(ctx, &pb_apache_http, BPF_F_CURRENT_CPU, e, sizeof(*e));
|
||||
}
|
||||
|
||||
/* Nettoyer */
|
||||
bpf_map_delete_elem(&apache_read_args_map, &pid_tgid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user