388 lines
10 KiB
Go
388 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// EBPFEvent represents an event captured by eBPF programs
|
|
type EBPFEvent struct {
|
|
Timestamp int64 `json:"timestamp"`
|
|
EventType string `json:"event_type"`
|
|
ProcessID int `json:"process_id"`
|
|
ProcessName string `json:"process_name"`
|
|
UserID int `json:"user_id"`
|
|
Data map[string]interface{} `json:"data"`
|
|
}
|
|
|
|
// EBPFTrace represents a collection of eBPF events for a specific investigation
|
|
type EBPFTrace struct {
|
|
TraceID string `json:"trace_id"`
|
|
StartTime time.Time `json:"start_time"`
|
|
EndTime time.Time `json:"end_time"`
|
|
Capability string `json:"capability"`
|
|
Events []EBPFEvent `json:"events"`
|
|
Summary string `json:"summary"`
|
|
EventCount int `json:"event_count"`
|
|
ProcessList []string `json:"process_list"`
|
|
}
|
|
|
|
// EBPFRequest represents a request to run eBPF monitoring
|
|
type EBPFRequest struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"` // "tracepoint", "kprobe", "kretprobe"
|
|
Target string `json:"target"` // tracepoint path or function name
|
|
Duration int `json:"duration"` // seconds
|
|
Filters map[string]string `json:"filters,omitempty"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
// EBPFManagerInterface defines the interface for eBPF managers
|
|
type EBPFManagerInterface interface {
|
|
GetCapabilities() map[string]bool
|
|
GetSummary() map[string]interface{}
|
|
StartEBPFProgram(req EBPFRequest) (string, error)
|
|
GetProgramResults(programID string) (*EBPFTrace, error)
|
|
StopProgram(programID string) error
|
|
ListActivePrograms() []string
|
|
}
|
|
|
|
// SimpleEBPFManager implements basic eBPF functionality using bpftrace
|
|
type SimpleEBPFManager struct {
|
|
programs map[string]*RunningProgram
|
|
programsLock sync.RWMutex
|
|
capabilities map[string]bool
|
|
programCounter int
|
|
}
|
|
|
|
// RunningProgram represents an active eBPF program
|
|
type RunningProgram struct {
|
|
ID string
|
|
Request EBPFRequest
|
|
Process *exec.Cmd
|
|
Events []EBPFEvent
|
|
StartTime time.Time
|
|
Cancel context.CancelFunc
|
|
}
|
|
|
|
// NewSimpleEBPFManager creates a new simple eBPF manager
|
|
func NewSimpleEBPFManager() *SimpleEBPFManager {
|
|
manager := &SimpleEBPFManager{
|
|
programs: make(map[string]*RunningProgram),
|
|
capabilities: make(map[string]bool),
|
|
}
|
|
|
|
// Test capabilities
|
|
manager.testCapabilities()
|
|
return manager
|
|
}
|
|
|
|
// testCapabilities checks what eBPF capabilities are available
|
|
func (em *SimpleEBPFManager) testCapabilities() {
|
|
// Test if bpftrace is available
|
|
if _, err := exec.LookPath("bpftrace"); err == nil {
|
|
em.capabilities["bpftrace"] = true
|
|
}
|
|
|
|
// Test root privileges (required for eBPF)
|
|
em.capabilities["root_access"] = os.Geteuid() == 0
|
|
|
|
// Test kernel version (simplified check)
|
|
cmd := exec.Command("uname", "-r")
|
|
output, err := cmd.Output()
|
|
if err == nil {
|
|
version := strings.TrimSpace(string(output))
|
|
em.capabilities["kernel_ebpf"] = strings.Contains(version, "4.") || strings.Contains(version, "5.") || strings.Contains(version, "6.")
|
|
} else {
|
|
em.capabilities["kernel_ebpf"] = false
|
|
}
|
|
|
|
log.Printf("eBPF capabilities: %+v", em.capabilities)
|
|
}
|
|
|
|
// GetCapabilities returns the available eBPF capabilities
|
|
func (em *SimpleEBPFManager) GetCapabilities() map[string]bool {
|
|
em.programsLock.RLock()
|
|
defer em.programsLock.RUnlock()
|
|
|
|
caps := make(map[string]bool)
|
|
for k, v := range em.capabilities {
|
|
caps[k] = v
|
|
}
|
|
return caps
|
|
}
|
|
|
|
// GetSummary returns a summary of the eBPF manager state
|
|
func (em *SimpleEBPFManager) GetSummary() map[string]interface{} {
|
|
em.programsLock.RLock()
|
|
defer em.programsLock.RUnlock()
|
|
|
|
return map[string]interface{}{
|
|
"capabilities": em.capabilities,
|
|
"active_programs": len(em.programs),
|
|
"program_ids": em.ListActivePrograms(),
|
|
}
|
|
}
|
|
|
|
// StartEBPFProgram starts a new eBPF monitoring program
|
|
func (em *SimpleEBPFManager) StartEBPFProgram(req EBPFRequest) (string, error) {
|
|
if !em.capabilities["bpftrace"] {
|
|
return "", fmt.Errorf("bpftrace not available")
|
|
}
|
|
|
|
if !em.capabilities["root_access"] {
|
|
return "", fmt.Errorf("root access required for eBPF programs")
|
|
}
|
|
|
|
em.programsLock.Lock()
|
|
defer em.programsLock.Unlock()
|
|
|
|
// Generate program ID
|
|
em.programCounter++
|
|
programID := fmt.Sprintf("prog_%d", em.programCounter)
|
|
|
|
// Create bpftrace script
|
|
script, err := em.generateBpftraceScript(req)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate script: %w", err)
|
|
}
|
|
|
|
// Start bpftrace process
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.Duration)*time.Second)
|
|
cmd := exec.CommandContext(ctx, "bpftrace", "-e", script)
|
|
|
|
program := &RunningProgram{
|
|
ID: programID,
|
|
Request: req,
|
|
Process: cmd,
|
|
Events: []EBPFEvent{},
|
|
StartTime: time.Now(),
|
|
Cancel: cancel,
|
|
}
|
|
|
|
// Start the program
|
|
if err := cmd.Start(); err != nil {
|
|
cancel()
|
|
return "", fmt.Errorf("failed to start bpftrace: %w", err)
|
|
}
|
|
|
|
em.programs[programID] = program
|
|
|
|
// Monitor the program in a goroutine
|
|
go em.monitorProgram(programID)
|
|
|
|
log.Printf("Started eBPF program %s for %s", programID, req.Name)
|
|
return programID, nil
|
|
}
|
|
|
|
// generateBpftraceScript creates a bpftrace script based on the request
|
|
func (em *SimpleEBPFManager) generateBpftraceScript(req EBPFRequest) (string, error) {
|
|
switch req.Type {
|
|
case "network":
|
|
return `
|
|
BEGIN {
|
|
printf("Starting network monitoring...\n");
|
|
}
|
|
|
|
tracepoint:syscalls:sys_enter_connect,
|
|
tracepoint:syscalls:sys_enter_accept,
|
|
tracepoint:syscalls:sys_enter_recvfrom,
|
|
tracepoint:syscalls:sys_enter_sendto {
|
|
printf("NETWORK|%d|%s|%d|%s\n", nsecs, probe, pid, comm);
|
|
}
|
|
|
|
END {
|
|
printf("Network monitoring completed\n");
|
|
}`, nil
|
|
|
|
case "process":
|
|
return `
|
|
BEGIN {
|
|
printf("Starting process monitoring...\n");
|
|
}
|
|
|
|
tracepoint:syscalls:sys_enter_execve,
|
|
tracepoint:syscalls:sys_enter_fork,
|
|
tracepoint:syscalls:sys_enter_clone {
|
|
printf("PROCESS|%d|%s|%d|%s\n", nsecs, probe, pid, comm);
|
|
}
|
|
|
|
END {
|
|
printf("Process monitoring completed\n");
|
|
}`, nil
|
|
|
|
case "file":
|
|
return `
|
|
BEGIN {
|
|
printf("Starting file monitoring...\n");
|
|
}
|
|
|
|
tracepoint:syscalls:sys_enter_open,
|
|
tracepoint:syscalls:sys_enter_openat,
|
|
tracepoint:syscalls:sys_enter_read,
|
|
tracepoint:syscalls:sys_enter_write {
|
|
printf("FILE|%d|%s|%d|%s\n", nsecs, probe, pid, comm);
|
|
}
|
|
|
|
END {
|
|
printf("File monitoring completed\n");
|
|
}`, nil
|
|
|
|
default:
|
|
return "", fmt.Errorf("unsupported eBPF program type: %s", req.Type)
|
|
}
|
|
}
|
|
|
|
// monitorProgram monitors a running eBPF program and collects events
|
|
func (em *SimpleEBPFManager) monitorProgram(programID string) {
|
|
em.programsLock.Lock()
|
|
program, exists := em.programs[programID]
|
|
if !exists {
|
|
em.programsLock.Unlock()
|
|
return
|
|
}
|
|
em.programsLock.Unlock()
|
|
|
|
// Wait for the program to complete
|
|
err := program.Process.Wait()
|
|
|
|
// Clean up
|
|
program.Cancel()
|
|
|
|
em.programsLock.Lock()
|
|
if err != nil {
|
|
log.Printf("eBPF program %s completed with error: %v", programID, err)
|
|
} else {
|
|
log.Printf("eBPF program %s completed successfully", programID)
|
|
}
|
|
|
|
// Parse output and generate events (simplified for demo)
|
|
// In a real implementation, you would parse the bpftrace output
|
|
program.Events = []EBPFEvent{
|
|
{
|
|
Timestamp: time.Now().Unix(),
|
|
EventType: program.Request.Type,
|
|
ProcessID: 0,
|
|
ProcessName: "example",
|
|
UserID: 0,
|
|
Data: map[string]interface{}{
|
|
"description": "Sample eBPF event",
|
|
"program_id": programID,
|
|
},
|
|
},
|
|
}
|
|
em.programsLock.Unlock()
|
|
|
|
log.Printf("Generated %d events for program %s", len(program.Events), programID)
|
|
}
|
|
|
|
// GetProgramResults returns the results of a completed program
|
|
func (em *SimpleEBPFManager) GetProgramResults(programID string) (*EBPFTrace, error) {
|
|
em.programsLock.RLock()
|
|
defer em.programsLock.RUnlock()
|
|
|
|
program, exists := em.programs[programID]
|
|
if !exists {
|
|
return nil, fmt.Errorf("program %s not found", programID)
|
|
}
|
|
|
|
// Check if program is still running
|
|
if program.Process.ProcessState == nil {
|
|
return nil, fmt.Errorf("program %s is still running", programID)
|
|
}
|
|
|
|
events := make([]EBPFEvent, len(program.Events))
|
|
copy(events, program.Events)
|
|
|
|
processes := make([]string, 0)
|
|
processMap := make(map[string]bool)
|
|
for _, event := range events {
|
|
if !processMap[event.ProcessName] {
|
|
processes = append(processes, event.ProcessName)
|
|
processMap[event.ProcessName] = true
|
|
}
|
|
}
|
|
|
|
trace := &EBPFTrace{
|
|
TraceID: programID,
|
|
StartTime: program.StartTime,
|
|
EndTime: time.Now(),
|
|
Capability: program.Request.Type,
|
|
Events: events,
|
|
EventCount: len(events),
|
|
ProcessList: processes,
|
|
Summary: fmt.Sprintf("Collected %d events for %s monitoring", len(events), program.Request.Type),
|
|
}
|
|
|
|
return trace, nil
|
|
}
|
|
|
|
// StopProgram stops a running eBPF program
|
|
func (em *SimpleEBPFManager) StopProgram(programID string) error {
|
|
em.programsLock.Lock()
|
|
defer em.programsLock.Unlock()
|
|
|
|
program, exists := em.programs[programID]
|
|
if !exists {
|
|
return fmt.Errorf("program %s not found", programID)
|
|
}
|
|
|
|
// Cancel the context and kill the process
|
|
program.Cancel()
|
|
if program.Process.Process != nil {
|
|
program.Process.Process.Kill()
|
|
}
|
|
|
|
delete(em.programs, programID)
|
|
log.Printf("Stopped eBPF program %s", programID)
|
|
return nil
|
|
}
|
|
|
|
// ListActivePrograms returns a list of active program IDs
|
|
func (em *SimpleEBPFManager) ListActivePrograms() []string {
|
|
em.programsLock.RLock()
|
|
defer em.programsLock.RUnlock()
|
|
|
|
programs := make([]string, 0, len(em.programs))
|
|
for id := range em.programs {
|
|
programs = append(programs, id)
|
|
}
|
|
return programs
|
|
}
|
|
|
|
// GetCommonEBPFRequests returns predefined eBPF programs for common use cases
|
|
func (em *SimpleEBPFManager) GetCommonEBPFRequests() []EBPFRequest {
|
|
return []EBPFRequest{
|
|
{
|
|
Name: "network_activity",
|
|
Type: "network",
|
|
Target: "syscalls:sys_enter_connect,sys_enter_accept,sys_enter_recvfrom,sys_enter_sendto",
|
|
Duration: 30,
|
|
Description: "Monitor network connections and data transfers",
|
|
},
|
|
{
|
|
Name: "process_activity",
|
|
Type: "process",
|
|
Target: "syscalls:sys_enter_execve,sys_enter_fork,sys_enter_clone",
|
|
Duration: 30,
|
|
Description: "Monitor process creation and execution",
|
|
},
|
|
{
|
|
Name: "file_access",
|
|
Type: "file",
|
|
Target: "syscalls:sys_enter_open,sys_enter_openat,sys_enter_read,sys_enter_write",
|
|
Duration: 30,
|
|
Description: "Monitor file system access and I/O operations",
|
|
},
|
|
}
|
|
}
|
|
|
|
// Helper functions - using system_info.go functions
|
|
// isRoot and checkKernelVersion are available from system_info.go
|