package loader import ( "os" "path/filepath" "testing" ) // TestFindLibaprPaths teste la recherche des chemins libapr pour Apache. func TestFindLibaprPaths(t *testing.T) { // Chemins attendus pour RedHat/CentOS/Rocky/AlmaLinux expectedPaths := []string{ "/usr/lib64/libapr-1.so.0", // RHEL/CentOS/Rocky/Alma 8/9/10 "/usr/lib/libapr-1.so.0", // Fallback } foundCount := 0 for _, path := range expectedPaths { stat, err := os.Stat(path) if err != nil { t.Logf("Path %s: %v", path, err) continue } if stat.IsDir() { t.Errorf("Path %s exists but is a directory, not a file", path) continue } // Vérifier que c'est un lien symbolique vers une librairie partagée if stat.Mode()&os.ModeSymlink != 0 { t.Logf("Path %s is a symlink (expected for libapr)", path) } foundCount++ } if foundCount == 0 { t.Log("No libapr found (this is OK if Apache is not installed)") } else { t.Logf("Found %d libapr path(s)", foundCount) } } // TestLibaprRedHatOnly vérifie que seuls les chemins RedHat sont recherchés. func TestLibaprRedHatOnly(t *testing.T) { // Chemins qui ne doivent PAS être recherchés (Debian/Ubuntu) debianPaths := []string{ "/usr/lib/x86_64-linux-gnu/libapr-1.so.0", "/usr/lib/apr/libapr-1.so.0", } for _, path := range debianPaths { // Ces chemins ne doivent pas être dans la liste de recherche if _, err := os.Stat(path); err == nil { t.Logf("Warning: Debian path %s exists but should not be used", path) } } // Vérifier que les chemins RedHat sont bien ceux utilisés redhatPaths := []string{ "/usr/lib64/libapr-1.so.0", "/usr/lib/libapr-1.so.0", } for _, path := range redhatPaths { // Ces chemins doivent être dans la liste de recherche if _, err := os.Stat(path); err == nil { t.Logf("Correct: RedHat path %s is available", path) } } } // TestAprSocketRecvSignature vérifie que la signature de apr_socket_recv // est correcte pour les uprobe/uretprobe. func TestAprSocketRecvSignature(t *testing.T) { // La signature est: apr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len) // - Premier paramètre: apr_socket_t *sock (non utilisé pour la capture) // - Deuxième paramètre: char *buf (pointeur vers buffer - capturé dans entry) // - Troisième paramètre: apr_size_t *len (pointeur vers taille - capturé dans entry) // Ce test documente la signature attendue // La valeur de retour est apr_status_t (0 = succès) t.Log("apr_socket_recv signature:") t.Log(" - Return: apr_status_t (int)") t.Log(" - Arg1: apr_socket_t *sock") t.Log(" - Arg2: char *buf (capturé via PT_REGS_PARM2)") t.Log(" - Arg3: apr_size_t *len (capturé via PT_REGS_PARM3, valeur déréférencée)") } // TestApacheEventStructure vérifie que la structure d'événement Apache // correspond aux attentes du parser HTTP. func TestApacheEventStructure(t *testing.T) { // Ce test documente la structure apache_http_event du BPF // Les champs doivent correspondre à ce qui est attendu par le consommateur Go t.Log("apache_http_event structure:") t.Log(" - pid_tgid: uint64 (PID + TGID)") t.Log(" - fd: uint32 (file descriptor)") t.Log(" - src_ip: uint32 (adresse IP source)") t.Log(" - src_port: uint16 (port source)") t.Log(" - timestamp_ns: uint64 (timestamp nanosecondes)") t.Log(" - data: char[4096] (données HTTP brutes)") t.Log(" - data_len: uint32 (taille des données)") } // TestApacheMapKeys vérifie les clés utilisées dans les maps Apache. func TestApacheMapKeys(t *testing.T) { // apache_http_pid_map: key=u32 (PID), value=u8 (enabled flag) // apr_socket_recv_args_map: key=u64 (pid_tgid), value=apr_socket_recv_args t.Log("apache_http_pid_map:") t.Log(" - Key: uint32 (PID)") t.Log(" - Value: uint8 (enabled flag: 0=disabled, 1=enabled)") t.Log("apr_socket_recv_args_map:") t.Log(" - Key: uint64 (pid_tgid)") t.Log(" - Value: struct { buf_ptr: uint64, len: uint32 }") } // TestApachePerfEventArray vérifie le nom du PerfEventArray Apache. func TestApachePerfEventArray(t *testing.T) { expectedName := "pb_apache_http" t.Logf("PerfEventArray name: %s", expectedName) t.Log("This must match the BPF program definition") } // TestApacheUniversalCompatibility vérifie que la méthode apr_socket_recv // est compatible avec tous les kernels 4.18+. func TestApacheUniversalCompatibility(t *testing.T) { // La méthode apr_socket_recv utilise des uprobes qui sont universels // et ne dépendent pas de tracepoints ou de fonctions kernel spécifiques t.Log("apr_socket_recv uprobe compatibility:") t.Log(" - Kernel min: 4.18+ (uprobe support)") t.Log(" - No dependency on tracepoints") t.Log(" - No dependency on kretprobes") t.Log(" - Works on all RHEL/CentOS/Rocky/AlmaLinux 8/9/10") } // TestFindLibaprInProc vérifie que nous pouvons trouver libapr dans /proc//maps. func TestFindLibaprInProc(t *testing.T) { // Chercher un processus Apache en cours d'exécution entries, err := os.ReadDir("/proc") if err != nil { t.Skip("Cannot read /proc") } for _, entry := range entries { if !entry.IsDir() { continue } pid := entry.Name() mapsPath := filepath.Join("/proc", pid, "maps") mapsData, err := os.ReadFile(mapsPath) if err != nil { continue } mapsContent := string(mapsData) if contains(mapsContent, "libapr-1.so") { t.Logf("Found libapr in PID %s maps", pid) return } } t.Log("No Apache process with libapr found (Apache may not be running)") } func contains(s, substr string) bool { return len(s) >= len(substr) && findSubstring(s, substr) } func findSubstring(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false }