Files
ja4-platform/services/ja4ebpf/bpf/uprobe_apache.c
Jacquin Antoine a2e0cfa2f3 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>
2026-04-20 14:11:56 +02:00

86 lines
3.2 KiB
C

/* uprobe_apache.c — Tracepoints syscall pour capturer le trafic HTTP depuis Apache httpd
*
* 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.
*
* ============================================================================
*/
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_types.h"
/* Taille maximale d'une capture recvfrom() */
#define MAX_RECV_SIZE 4096
/* ============================================================================
* kretprobe_sys_exit_recvfrom — Sortie du syscall recvfrom
*
* 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("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;
/* Vérifier si ce PID est dans la map apache_pid_map */
__u32 pid_key = pid;
__u8 *enabled = bpf_map_lookup_elem(&apache_pid_map, &pid_key);
if (!enabled || *enabled == 0) {
return 0; /* Pas un PID Apache, ignore */
}
/* Obtenir la valeur de retour (nombre d'octets reçus) */
long retval = PT_REGS_RC(ctx);
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) {
return 0;
}
/* Initialiser l'événement */
e->pid_tgid = pid_tgid;
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;
/* 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));
}
}
return 0;
}
char LICENSE[] SEC("license") = "GPL";