feat(ebpf): Apache HTTP capture implementation (WIP on Rocky 10)
- Implemented Apache HTTP capture using recvfrom syscall (model identical to nginx) - Added sys_enter_recvfrom + kretprobe __x64_sys_recvfrom approach - Renamed Apache BPF maps (apache_http_pid_map, apache_http_recv_args_map) to avoid conflicts with nginx - Added support for recvfrom and recvmsg syscalls (recvmsg support incomplete) Test results: - Rocky 9 (kernel 5.14): nginx HTTP capture works perfectly with full headers - Rocky 10 (kernel 6.12): Apache HTTP capture NOT working (headers=0) - CentOS 8 (kernel 4.18): Apache HTTP capture NOT working (headers=0) Root cause: Apache event MPM uses async epoll model that doesn't trigger recvfrom syscalls the same way as nginx. Further investigation needed for Apache-specific capture methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -1,7 +1,6 @@
|
|||||||
/* uprobe_apache.c — Kretprobe pour capturer le trafic HTTP depuis Apache httpd
|
/* uprobe_apache.c — Capture HTTP depuis Apache httpd via recvfrom
|
||||||
*
|
*
|
||||||
* Cette version utilise kretprobe sur __x64_sys_recvfrom (identique à nginx)
|
* Identique à nginx : sys_enter_recvfrom + kretprobe __x64_sys_recvfrom
|
||||||
* pour capturer les appels système recvfrom() du serveur Apache httpd.
|
|
||||||
*
|
*
|
||||||
* ============================================================================
|
* ============================================================================
|
||||||
*/
|
*/
|
||||||
@ -11,45 +10,74 @@
|
|||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "bpf_types.h"
|
#include "bpf_types.h"
|
||||||
|
|
||||||
/* Taille maximale d'une capture recvfrom() */
|
|
||||||
#define MAX_RECV_SIZE 4096
|
#define MAX_RECV_SIZE 4096
|
||||||
|
|
||||||
/* ============================================================================
|
struct recvfrom_args {
|
||||||
* kretprobe_sys_exit_recvfrom — Sortie du syscall recvfrom
|
__s32 sockfd;
|
||||||
*
|
__u64 buf_ptr;
|
||||||
* Capture les données reçues et les envoie vers pb_apache_http.
|
__u64 len;
|
||||||
* Utilise kretprobe pour contourner les limitations de tracepoint exit.
|
__s64 flags;
|
||||||
* ============================================================================
|
} __attribute__((packed));
|
||||||
*/
|
|
||||||
|
/* sys_enter_recvfrom - identique à nginx */
|
||||||
|
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;
|
||||||
|
|
||||||
|
__u8 *enabled = bpf_map_lookup_elem(&apache_http_pid_map, &pid);
|
||||||
|
if (!enabled || *enabled == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct recvfrom_args args = {};
|
||||||
|
args.sockfd = (__s32)ctx->args[0];
|
||||||
|
args.buf_ptr = (__u64)ctx->args[1];
|
||||||
|
args.len = (__u64)ctx->args[2];
|
||||||
|
args.flags = (__s64)ctx->args[3];
|
||||||
|
|
||||||
|
bpf_map_update_elem(&apache_http_recv_args_map, &pid_tgid, &args, BPF_ANY);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kretprobe __x64_sys_recvfrom - identique à nginx */
|
||||||
SEC("kretprobe/__x64_sys_recvfrom")
|
SEC("kretprobe/__x64_sys_recvfrom")
|
||||||
int kretprobe_sys_exit_recvfrom(struct pt_regs *ctx)
|
int kretprobe_sys_exit_recvfrom(struct pt_regs *ctx)
|
||||||
{
|
{
|
||||||
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
||||||
__u32 pid = pid_tgid >> 32;
|
__u32 pid = pid_tgid >> 32;
|
||||||
|
|
||||||
/* Vérifier si ce PID est dans la map apache_http_pid_map */
|
__u8 *enabled = bpf_map_lookup_elem(&apache_http_pid_map, &pid);
|
||||||
__u32 pid_key = pid;
|
|
||||||
__u8 *enabled = bpf_map_lookup_elem(&apache_http_pid_map, &pid_key);
|
|
||||||
if (!enabled || *enabled == 0) {
|
if (!enabled || *enabled == 0) {
|
||||||
return 0; /* Pas un PID Apache, ignore */
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct recvfrom_args *args = bpf_map_lookup_elem(&apache_http_recv_args_map, &pid_tgid);
|
||||||
|
if (!args) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Obtenir la valeur de retour (nombre d'octets reçus) */
|
|
||||||
long retval = PT_REGS_RC(ctx);
|
long retval = PT_REGS_RC(ctx);
|
||||||
if (retval < 0 || retval > MAX_RECV_SIZE) {
|
if (retval <= 0) {
|
||||||
/* Erreur ou trop de données */
|
bpf_map_delete_elem(&apache_http_recv_args_map, &pid_tgid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Préparer l'événement Apache HTTP */
|
__u32 data_len = retval;
|
||||||
struct apache_http_event *e = bpf_map_lookup_elem(&__apache_buf, &pid_tgid);
|
if (data_len > MAX_RECV_SIZE)
|
||||||
|
data_len = MAX_RECV_SIZE;
|
||||||
|
|
||||||
|
__u32 zero = 0;
|
||||||
|
struct apache_http_event *e = bpf_map_lookup_elem(&__apache_buf, &zero);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
|
bpf_map_delete_elem(&apache_http_recv_args_map, &pid_tgid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialiser l'événement */
|
|
||||||
e->pid_tgid = pid_tgid;
|
e->pid_tgid = pid_tgid;
|
||||||
e->fd = 0;
|
e->fd = args->sockfd;
|
||||||
e->src_ip = 0;
|
e->src_ip = 0;
|
||||||
e->src_port = 0;
|
e->src_port = 0;
|
||||||
e->timestamp_ns = bpf_ktime_get_ns();
|
e->timestamp_ns = bpf_ktime_get_ns();
|
||||||
@ -59,23 +87,17 @@ int kretprobe_sys_exit_recvfrom(struct pt_regs *ctx)
|
|||||||
e->body_len = 0;
|
e->body_len = 0;
|
||||||
e->data_len = 0;
|
e->data_len = 0;
|
||||||
|
|
||||||
/* Copier les données depuis le buffer utilisateur (2ème argument: RSI) */
|
if (data_len > 0) {
|
||||||
__u64 buf_ptr = PT_REGS_PARM2(ctx);
|
__u32 copy_len = data_len;
|
||||||
|
if (copy_len > sizeof(e->data))
|
||||||
/* Limiter la copie à retval octets */
|
copy_len = sizeof(e->data);
|
||||||
__u64 copy_size = retval;
|
bpf_probe_read_user(e->data, copy_len, (void *)args->buf_ptr);
|
||||||
if (copy_size > sizeof(e->data)) {
|
e->data_len = copy_len;
|
||||||
copy_size = sizeof(e->data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copy_size > 0) {
|
|
||||||
__u64 bytes_read = bpf_probe_read_user_str(e->data, copy_size, (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));
|
bpf_perf_event_output(ctx, &pb_apache_http, BPF_F_CURRENT_CPU, e, sizeof(*e));
|
||||||
}
|
|
||||||
}
|
bpf_map_delete_elem(&apache_http_recv_args_map, &pid_tgid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -539,14 +539,21 @@ func findNginxPIDs() ([]uint32, error) {
|
|||||||
// kernel sys_enter_read et kretprobe __x64_sys_read.
|
// kernel sys_enter_read et kretprobe __x64_sys_read.
|
||||||
// Le PID Apache est ajouté à la map apache_pid_map pour filtrer les appels read().
|
// Le PID Apache est ajouté à la map apache_pid_map pour filtrer les appels read().
|
||||||
func (l *Loader) AttachUprobesApache() error {
|
func (l *Loader) AttachUprobesApache() error {
|
||||||
// Utilisation de Kretprobe pour __x64_sys_recvfrom (identique à nginx)
|
// Identique à nginx : sys_enter_recvfrom + kretprobe __x64_sys_recvfrom
|
||||||
// Apache httpd utilise recvfrom() pour lire les requêtes HTTP
|
|
||||||
kp, err := link.Kretprobe("__x64_sys_recvfrom",
|
kpEnter, err := link.Tracepoint("syscalls", "sys_enter_recvfrom",
|
||||||
|
l.apacheObjs.TpSysEnterRecvfrom, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("attachement tracepoint sys_enter_recvfrom: %w", err)
|
||||||
|
}
|
||||||
|
l.uprobeLinks = append(l.uprobeLinks, kpEnter)
|
||||||
|
|
||||||
|
kpExit, err := link.Kretprobe("__x64_sys_recvfrom",
|
||||||
l.apacheObjs.KretprobeSysExitRecvfrom, &link.KprobeOptions{})
|
l.apacheObjs.KretprobeSysExitRecvfrom, &link.KprobeOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("attachement kretprobe recvfrom: %w", err)
|
return fmt.Errorf("attachement kretprobe __x64_sys_recvfrom: %w", err)
|
||||||
}
|
}
|
||||||
l.uprobeLinks = append(l.uprobeLinks, kp)
|
l.uprobeLinks = append(l.uprobeLinks, kpExit)
|
||||||
|
|
||||||
// Trouver les PIDs Apache httpd en cours d'exécution
|
// Trouver les PIDs Apache httpd en cours d'exécution
|
||||||
pids, err := findApachePIDs()
|
pids, err := findApachePIDs()
|
||||||
|
|||||||
Reference in New Issue
Block a user