Fixes "permission denied" error when attaching tracepoint sys_exit_recvfrom
on Rocky Linux 9 (kernel 5.14+). The tracepoint exit has stricter permissions
than entry tracepoints.
Changes:
- BPF: SEC("tp/syscalls/sys_exit_recvfrom") → SEC("kretprobe/__x64_sys_recvfrom")
- BPF: Extract retval using PT_REGS_RC(ctx) instead of ctx->ret
- Loader: link.Tracepoint() → link.Kretprobe()
- Add nginxPidMap for filtering recvfrom calls by nginx PID
Validation:
- All HTTP fields captured without truncation (path up to 39 chars, query up to 244 chars)
- Custom headers (X-Request-ID, X-Custom-Header) fully captured
- Unit tests added and passing (TestKretprobeRecvfromAttachment, TestKretprobeVsTracepoint)
- ClickHouse validation complete: http_logs and http_logs_raw tables verified
Tested on:
- Rocky Linux 9 (kernel 5.14+)
- bpftool shows: kprobe name tp_sys_exit_recvfrom (kretprobe active)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
253 lines
6.3 KiB
Go
253 lines
6.3 KiB
Go
// Code generated by bpf2go; DO NOT EDIT.
|
|
//go:build 386 || amd64
|
|
|
|
package loader
|
|
|
|
import (
|
|
"bytes"
|
|
_ "embed"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/cilium/ebpf"
|
|
)
|
|
|
|
type Ja4NginxAcceptEvent struct {
|
|
PidTgid uint64
|
|
Fd uint32
|
|
SrcIp uint32
|
|
SrcPort uint16
|
|
TimestampNs uint64
|
|
}
|
|
|
|
type Ja4NginxAcceptKey struct {
|
|
PidTgid uint64
|
|
Fd uint32
|
|
}
|
|
|
|
type Ja4NginxHttpPlainEvent struct {
|
|
Payload [4096]uint8
|
|
SrcIp uint32
|
|
DstIp uint32
|
|
SrcPort uint16
|
|
DstPort uint16
|
|
PayloadLen uint16
|
|
TimestampNs uint64
|
|
}
|
|
|
|
type Ja4NginxNginxHttpEvent struct {
|
|
PidTgid uint64
|
|
Fd uint32
|
|
SrcIp uint32
|
|
SrcPort uint16
|
|
TimestampNs uint64
|
|
HttpMethod [16]uint8
|
|
Uri [256]uint8
|
|
Query [128]uint8
|
|
Data [3640]uint8
|
|
MethodLen uint32
|
|
UriLen uint32
|
|
QueryLen uint32
|
|
BodyLen uint32
|
|
DataLen uint32
|
|
}
|
|
|
|
type Ja4NginxNginxReadArgs struct {
|
|
Fd int32
|
|
BufPtr uint64
|
|
Count uint64
|
|
}
|
|
|
|
type Ja4NginxSslConnInfo struct {
|
|
Fd uint32
|
|
SrcIp uint32
|
|
SrcPort uint16
|
|
}
|
|
|
|
type Ja4NginxSslDataEvent struct {
|
|
PidTgid uint64
|
|
Fd uint32
|
|
SrcIp uint32
|
|
SrcPort uint16
|
|
Data [4096]uint8
|
|
DataLen uint32
|
|
TimestampNs uint64
|
|
Direction uint8
|
|
}
|
|
|
|
type Ja4NginxSslReadArgs struct {
|
|
SslPtr uint64
|
|
BufPtr uint64
|
|
Num uint32
|
|
}
|
|
|
|
type Ja4NginxTlsHelloEvent struct {
|
|
Payload [2048]uint8
|
|
SrcIp uint32
|
|
DstIp uint32
|
|
SrcPort uint16
|
|
DstPort uint16
|
|
PayloadLen uint16
|
|
TimestampNs uint64
|
|
}
|
|
|
|
// LoadJa4Nginx returns the embedded CollectionSpec for Ja4Nginx.
|
|
func LoadJa4Nginx() (*ebpf.CollectionSpec, error) {
|
|
reader := bytes.NewReader(_Ja4NginxBytes)
|
|
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't load Ja4Nginx: %w", err)
|
|
}
|
|
|
|
return spec, err
|
|
}
|
|
|
|
// LoadJa4NginxObjects loads Ja4Nginx and converts it into a struct.
|
|
//
|
|
// The following types are suitable as obj argument:
|
|
//
|
|
// *Ja4NginxObjects
|
|
// *Ja4NginxPrograms
|
|
// *Ja4NginxMaps
|
|
//
|
|
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
|
func LoadJa4NginxObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
|
spec, err := LoadJa4Nginx()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return spec.LoadAndAssign(obj, opts)
|
|
}
|
|
|
|
// Ja4NginxSpecs contains maps and programs before they are loaded into the kernel.
|
|
//
|
|
// It can be passed ebpf.CollectionSpec.Assign.
|
|
type Ja4NginxSpecs struct {
|
|
Ja4NginxProgramSpecs
|
|
Ja4NginxMapSpecs
|
|
}
|
|
|
|
// Ja4NginxSpecs contains programs before they are loaded into the kernel.
|
|
//
|
|
// It can be passed ebpf.CollectionSpec.Assign.
|
|
type Ja4NginxProgramSpecs struct {
|
|
UprobeReadEntry *ebpf.ProgramSpec `ebpf:"uprobe_read_entry"`
|
|
UretprobeReadExit *ebpf.ProgramSpec `ebpf:"uretprobe_read_exit"`
|
|
TpSysEnterRecvfrom *ebpf.ProgramSpec `ebpf:"tp_sys_enter_recvfrom"`
|
|
TpSysExitRecvfrom *ebpf.ProgramSpec `ebpf:"tp_sys_exit_recvfrom"`
|
|
}
|
|
|
|
// Ja4NginxMapSpecs contains maps before they are loaded into the kernel.
|
|
//
|
|
// It can be passed ebpf.CollectionSpec.Assign.
|
|
type Ja4NginxMapSpecs struct {
|
|
HttpBuf *ebpf.MapSpec `ebpf:"__http_buf"`
|
|
NginxBuf *ebpf.MapSpec `ebpf:"__nginx_buf"`
|
|
SslBuf *ebpf.MapSpec `ebpf:"__ssl_buf"`
|
|
TlsBuf *ebpf.MapSpec `ebpf:"__tls_buf"`
|
|
AcceptMap *ebpf.MapSpec `ebpf:"accept_map"`
|
|
FdConnMap *ebpf.MapSpec `ebpf:"fd_conn_map"`
|
|
NginxPidMap *ebpf.MapSpec `ebpf:"nginx_pid_map"`
|
|
NginxReadArgsMap *ebpf.MapSpec `ebpf:"nginx_read_args_map"`
|
|
PbAccept *ebpf.MapSpec `ebpf:"pb_accept"`
|
|
PbGinxHttp *ebpf.MapSpec `ebpf:"pb_ginx_http"`
|
|
PbHttpPlain *ebpf.MapSpec `ebpf:"pb_http_plain"`
|
|
PbSslData *ebpf.MapSpec `ebpf:"pb_ssl_data"`
|
|
PbTcpSyn *ebpf.MapSpec `ebpf:"pb_tcp_syn"`
|
|
PbTlsHello *ebpf.MapSpec `ebpf:"pb_tls_hello"`
|
|
SslArgsMap *ebpf.MapSpec `ebpf:"ssl_args_map"`
|
|
SslConnMap *ebpf.MapSpec `ebpf:"ssl_conn_map"`
|
|
}
|
|
|
|
// Ja4NginxObjects contains all objects after they have been loaded into the kernel.
|
|
//
|
|
// It can be passed to LoadJa4NginxObjects or ebpf.CollectionSpec.LoadAndAssign.
|
|
type Ja4NginxObjects struct {
|
|
Ja4NginxPrograms
|
|
Ja4NginxMaps
|
|
}
|
|
|
|
func (o *Ja4NginxObjects) Close() error {
|
|
return _Ja4NginxClose(
|
|
&o.Ja4NginxPrograms,
|
|
&o.Ja4NginxMaps,
|
|
)
|
|
}
|
|
|
|
// Ja4NginxMaps contains all maps after they have been loaded into the kernel.
|
|
//
|
|
// It can be passed to LoadJa4NginxObjects or ebpf.CollectionSpec.LoadAndAssign.
|
|
type Ja4NginxMaps struct {
|
|
HttpBuf *ebpf.Map `ebpf:"__http_buf"`
|
|
NginxBuf *ebpf.Map `ebpf:"__nginx_buf"`
|
|
SslBuf *ebpf.Map `ebpf:"__ssl_buf"`
|
|
TlsBuf *ebpf.Map `ebpf:"__tls_buf"`
|
|
AcceptMap *ebpf.Map `ebpf:"accept_map"`
|
|
FdConnMap *ebpf.Map `ebpf:"fd_conn_map"`
|
|
NginxPidMap *ebpf.Map `ebpf:"nginx_pid_map"`
|
|
NginxReadArgsMap *ebpf.Map `ebpf:"nginx_read_args_map"`
|
|
PbAccept *ebpf.Map `ebpf:"pb_accept"`
|
|
PbGinxHttp *ebpf.Map `ebpf:"pb_ginx_http"`
|
|
PbHttpPlain *ebpf.Map `ebpf:"pb_http_plain"`
|
|
PbSslData *ebpf.Map `ebpf:"pb_ssl_data"`
|
|
PbTcpSyn *ebpf.Map `ebpf:"pb_tcp_syn"`
|
|
PbTlsHello *ebpf.Map `ebpf:"pb_tls_hello"`
|
|
SslArgsMap *ebpf.Map `ebpf:"ssl_args_map"`
|
|
SslConnMap *ebpf.Map `ebpf:"ssl_conn_map"`
|
|
}
|
|
|
|
func (m *Ja4NginxMaps) Close() error {
|
|
return _Ja4NginxClose(
|
|
m.HttpBuf,
|
|
m.NginxBuf,
|
|
m.SslBuf,
|
|
m.TlsBuf,
|
|
m.AcceptMap,
|
|
m.FdConnMap,
|
|
m.NginxPidMap,
|
|
m.NginxReadArgsMap,
|
|
m.PbAccept,
|
|
m.PbGinxHttp,
|
|
m.PbHttpPlain,
|
|
m.PbSslData,
|
|
m.PbTcpSyn,
|
|
m.PbTlsHello,
|
|
m.SslArgsMap,
|
|
m.SslConnMap,
|
|
)
|
|
}
|
|
|
|
// Ja4NginxPrograms contains all programs after they have been loaded into the kernel.
|
|
//
|
|
// It can be passed to LoadJa4NginxObjects or ebpf.CollectionSpec.LoadAndAssign.
|
|
type Ja4NginxPrograms struct {
|
|
UprobeReadEntry *ebpf.Program `ebpf:"uprobe_read_entry"`
|
|
UretprobeReadExit *ebpf.Program `ebpf:"uretprobe_read_exit"`
|
|
TpSysEnterRecvfrom *ebpf.Program `ebpf:"tp_sys_enter_recvfrom"`
|
|
TpSysExitRecvfrom *ebpf.Program `ebpf:"tp_sys_exit_recvfrom"`
|
|
}
|
|
|
|
func (p *Ja4NginxPrograms) Close() error {
|
|
return _Ja4NginxClose(
|
|
p.UprobeReadEntry,
|
|
p.UretprobeReadExit,
|
|
p.TpSysEnterRecvfrom,
|
|
p.TpSysExitRecvfrom,
|
|
)
|
|
}
|
|
|
|
func _Ja4NginxClose(closers ...io.Closer) error {
|
|
for _, closer := range closers {
|
|
if err := closer.Close(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Do not access this directly.
|
|
//
|
|
//go:embed ja4nginx_x86_bpfel.o
|
|
var _Ja4NginxBytes []byte
|