fix(ja4ebpf): correct BPF struct byte offsets and regenerate SSL_write programs
Fix two critical offset bugs introduced when ip_total_length was added to tcp_syn_event: tcp_options_raw offset 21→23 and tcp_options_len offset 61→63, plus minimum size check 70→72. Fix ssl_data_event direction field offset from 4118 (inside timestamp_ns) to 4126. Simplify attachSSLWrite to use generated objects directly instead of dynamic spec loading. Regenerate BPF objects with SSL_write uprobe programs included. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -309,9 +309,9 @@ func consumeSynEvents(ctx context.Context, rd *perf.Reader, mgr *correlation.Man
|
||||
// src_ip(4)+dst_ip(4)+src_port(2)+dst_port(2)+ttl(1)+df_bit(1)+ip_id(2)+
|
||||
// ip_total_length(2)+window_size(2)+window_scale(1)+mss(2)+tcp_options_raw[40]+
|
||||
// tcp_options_len(1)+timestamp_ns(8)
|
||||
// offsets: 0 4 8 10 12 13 14 16 18 19 21 61
|
||||
// total = 62 + 8 = 70
|
||||
if len(record.RawSample) < 70 {
|
||||
// offsets: 0 4 8 10 12 13 14 16 18 20 21 23 63
|
||||
// total = 64 + 8 = 72
|
||||
if len(record.RawSample) < 72 {
|
||||
continue
|
||||
}
|
||||
data := record.RawSample
|
||||
@ -342,12 +342,12 @@ func consumeSynEvents(ctx context.Context, rd *perf.Reader, mgr *correlation.Man
|
||||
ipTotalLength := binary.LittleEndian.Uint16(data[16:18])
|
||||
windowSize := binary.LittleEndian.Uint16(data[18:20])
|
||||
|
||||
optLen := int(data[61])
|
||||
optLen := int(data[63])
|
||||
if optLen > 40 {
|
||||
optLen = 40
|
||||
}
|
||||
tcpOpts := make([]byte, optLen)
|
||||
copy(tcpOpts, data[21:21+optLen])
|
||||
copy(tcpOpts, data[23:23+optLen])
|
||||
|
||||
// Analyser les options TCP brutes pour extraire MSS et Window Scale
|
||||
mss, windowScale := parseTCPOptions(tcpOpts)
|
||||
@ -499,12 +499,13 @@ func consumeSSLEvents(ctx context.Context, rd *perf.Reader, mgr *correlation.Man
|
||||
srcIPRaw := binary.LittleEndian.Uint32(data[12:16])
|
||||
srcPort := binary.LittleEndian.Uint16(data[16:18])
|
||||
|
||||
// data[4096] commence à offset 18, data_len à offset 4114, direction à offset 4118
|
||||
if len(data) < 4119 {
|
||||
// data[4096] commence à offset 18, data_len à offset 4114,
|
||||
// timestamp_ns à offset 4118, direction à offset 4126
|
||||
if len(data) < 4127 {
|
||||
continue
|
||||
}
|
||||
dataLen := binary.LittleEndian.Uint32(data[4114:4118])
|
||||
direction := data[4118] // 0 = SSL_read (client→serveur), 1 = SSL_write (serveur→client)
|
||||
direction := data[4126] // 0 = SSL_read (client→serveur), 1 = SSL_write (serveur→client)
|
||||
if dataLen > 4096 {
|
||||
dataLen = 4096
|
||||
}
|
||||
|
||||
@ -111,7 +111,9 @@ type Ja4SslProgramSpecs struct {
|
||||
KretprobeAccept4Exit *ebpf.ProgramSpec `ebpf:"kretprobe_accept4_exit"`
|
||||
UprobeSslReadEntry *ebpf.ProgramSpec `ebpf:"uprobe_ssl_read_entry"`
|
||||
UprobeSslSetFd *ebpf.ProgramSpec `ebpf:"uprobe_ssl_set_fd"`
|
||||
UprobeSslWriteEntry *ebpf.ProgramSpec `ebpf:"uprobe_ssl_write_entry"`
|
||||
UretprobeSslReadExit *ebpf.ProgramSpec `ebpf:"uretprobe_ssl_read_exit"`
|
||||
UretprobeSslWriteExit *ebpf.ProgramSpec `ebpf:"uretprobe_ssl_write_exit"`
|
||||
}
|
||||
|
||||
// Ja4SslMapSpecs contains maps before they are loaded into the kernel.
|
||||
@ -193,7 +195,9 @@ type Ja4SslPrograms struct {
|
||||
KretprobeAccept4Exit *ebpf.Program `ebpf:"kretprobe_accept4_exit"`
|
||||
UprobeSslReadEntry *ebpf.Program `ebpf:"uprobe_ssl_read_entry"`
|
||||
UprobeSslSetFd *ebpf.Program `ebpf:"uprobe_ssl_set_fd"`
|
||||
UprobeSslWriteEntry *ebpf.Program `ebpf:"uprobe_ssl_write_entry"`
|
||||
UretprobeSslReadExit *ebpf.Program `ebpf:"uretprobe_ssl_read_exit"`
|
||||
UretprobeSslWriteExit *ebpf.Program `ebpf:"uretprobe_ssl_write_exit"`
|
||||
}
|
||||
|
||||
func (p *Ja4SslPrograms) Close() error {
|
||||
@ -202,7 +206,9 @@ func (p *Ja4SslPrograms) Close() error {
|
||||
p.KretprobeAccept4Exit,
|
||||
p.UprobeSslReadEntry,
|
||||
p.UprobeSslSetFd,
|
||||
p.UprobeSslWriteEntry,
|
||||
p.UretprobeSslReadExit,
|
||||
p.UretprobeSslWriteExit,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -266,10 +266,9 @@ func (l *Loader) AttachUprobes(sslLibPath string) error {
|
||||
l.uprobeLinks = append(l.uprobeLinks, readExitLink)
|
||||
|
||||
// SSL_write — capture les réponses HTTP du serveur (direction=1)
|
||||
// Les programmes BPF uprobe/SSL_write et uretprobe/SSL_write sont
|
||||
// chargés depuis les objets Ja4Ssl. Si les objets BPF n'ont pas été
|
||||
// régénérés (pas de clang sur le host), ces programmes sont absents.
|
||||
_ = l.attachSSLWrite(ex)
|
||||
if err := l.attachSSLWrite(ex); err != nil {
|
||||
return fmt.Errorf("attachement SSL_write: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -293,43 +292,17 @@ func (l *Loader) AttachAcceptProbe() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// attachSSLWrite tente d'attacher les uprobes SSL_write pour capturer
|
||||
// les réponses HTTP du serveur. Si les programmes BPF SSL_write ne sont
|
||||
// pas disponibles (objets non régénérés), retourne nil sans bloquer.
|
||||
// attachSSLWrite attache les uprobes SSL_write pour capturer
|
||||
// les réponses HTTP du serveur (direction=1).
|
||||
func (l *Loader) attachSSLWrite(ex *link.Executable) error {
|
||||
// Charger la collection spec embarquée pour vérifier si SSL_write existe
|
||||
spec, err := LoadJa4Ssl()
|
||||
entryLink, err := ex.Uprobe("SSL_write", l.sslObjs.UprobeSslWriteEntry, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
entrySpec, hasEntry := spec.Programs["uprobe_ssl_write_entry"]
|
||||
exitSpec, hasExit := spec.Programs["uretprobe_ssl_write_exit"]
|
||||
if !hasEntry || !hasExit {
|
||||
return nil // programmes SSL_write absents — BPF non régénéré
|
||||
}
|
||||
|
||||
writeEntry, err := ebpf.NewProgram(entrySpec)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
writeExit, err := ebpf.NewProgram(exitSpec)
|
||||
if err != nil {
|
||||
writeEntry.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
entryLink, err := ex.Uprobe("SSL_write", writeEntry, nil)
|
||||
if err != nil {
|
||||
writeEntry.Close()
|
||||
writeExit.Close()
|
||||
return fmt.Errorf("attachement uprobe SSL_write (entry): %w", err)
|
||||
}
|
||||
l.uprobeLinks = append(l.uprobeLinks, entryLink)
|
||||
|
||||
exitLink, err := ex.Uretprobe("SSL_write", writeExit, nil)
|
||||
exitLink, err := ex.Uretprobe("SSL_write", l.sslObjs.UretprobeSslWriteExit, nil)
|
||||
if err != nil {
|
||||
writeExit.Close()
|
||||
return fmt.Errorf("attachement uretprobe SSL_write (exit): %w", err)
|
||||
}
|
||||
l.uprobeLinks = append(l.uprobeLinks, exitLink)
|
||||
|
||||
Reference in New Issue
Block a user