diff --git a/services/ja4ebpf/bpf/bpf_types.h b/services/ja4ebpf/bpf/bpf_types.h index da17f7f..6fe4310 100644 --- a/services/ja4ebpf/bpf/bpf_types.h +++ b/services/ja4ebpf/bpf/bpf_types.h @@ -54,7 +54,9 @@ struct tcp_syn_event { struct tls_hello_event { __u8 payload[2048]; /* payload ClientHello brut (offset 0) */ __u32 src_ip; /* adresse source (host byte order) */ + __u32 dst_ip; /* adresse destination (host byte order) */ __u16 src_port; /* port source (host byte order) */ + __u16 dst_port; /* port destination (host byte order) */ __u16 payload_len; /* longueur effective du payload */ __u64 timestamp_ns; /* horodatage kernel */ } __attribute__((packed)); diff --git a/services/ja4ebpf/bpf/tc_capture.c b/services/ja4ebpf/bpf/tc_capture.c index 851a668..926df32 100644 --- a/services/ja4ebpf/bpf/tc_capture.c +++ b/services/ja4ebpf/bpf/tc_capture.c @@ -209,12 +209,16 @@ int capture_tc(struct __sk_buff *ctx) return TC_ACT_OK; tls_evt->src_ip = 0; + tls_evt->dst_ip = 0; tls_evt->src_port = 0; + tls_evt->dst_port = 0; tls_evt->payload_len = 0; tls_evt->timestamp_ns = 0; tls_evt->src_ip = bpf_ntohl(src_ip); + tls_evt->dst_ip = bpf_ntohl(dst_ip); tls_evt->src_port = src_port; + tls_evt->dst_port = dst_port; tls_evt->timestamp_ns = bpf_ktime_get_ns(); /* Copie via bpf_skb_load_bytes avec tailles constantes en cascade. diff --git a/services/ja4ebpf/cmd/ja4ebpf/main.go b/services/ja4ebpf/cmd/ja4ebpf/main.go index 638084b..115233e 100644 --- a/services/ja4ebpf/cmd/ja4ebpf/main.go +++ b/services/ja4ebpf/cmd/ja4ebpf/main.go @@ -394,16 +394,18 @@ func consumeTLSEvents(ctx context.Context, rd *perf.Reader, mgr *correlation.Man } // struct tls_hello_event (packed): - // src_ip(4) + src_port(2) + payload[2048] + payload_len(2) + timestamp_ns(8) - // offsets: 0 4 6 2054 2056 - if len(record.RawSample) < 2064 { + // payload[2048] + src_ip(4) + dst_ip(4) + src_port(2) + dst_port(2) + payload_len(2) + timestamp_ns(8) + // offsets: 0 2048 2052 2056 2058 2060 2062 + if len(record.RawSample) < 2070 { continue } data := record.RawSample srcIPRaw := binary.LittleEndian.Uint32(data[2048:2052]) - srcPort := binary.LittleEndian.Uint16(data[2052:2054]) - payloadLen := binary.LittleEndian.Uint16(data[2054:2056]) + dstIPRaw := binary.LittleEndian.Uint32(data[2052:2056]) + srcPort := binary.LittleEndian.Uint16(data[2056:2058]) + dstPort := binary.LittleEndian.Uint16(data[2058:2060]) + payloadLen := binary.LittleEndian.Uint16(data[2060:2062]) if int(payloadLen) > 2048 { payloadLen = 2048 @@ -418,6 +420,12 @@ func consumeTLSEvents(ctx context.Context, rd *perf.Reader, mgr *correlation.Man key.SrcIP[3] = byte(srcIPRaw) key.SrcPort = srcPort + var tlsDstIP [4]byte + tlsDstIP[0] = byte(dstIPRaw >> 24) + tlsDstIP[1] = byte(dstIPRaw >> 16) + tlsDstIP[2] = byte(dstIPRaw >> 8) + tlsDstIP[3] = byte(dstIPRaw) + // Parser le ClientHello et calculer JA4 ch, err := parser.ParseClientHello(payload) if err != nil { @@ -460,9 +468,12 @@ func consumeTLSEvents(ctx context.Context, rd *perf.Reader, mgr *correlation.Man TLSVersion: tlsVer, Timestamp: time.Now(), } - // Corréler si L3/L4 est déjà présent - if s.L3L4 != nil { - _ = s.L3L4 // corrélation implicite par présence des deux champs + // Peupler L3/L4 si absent (SYN non capturé, TLS arrivé en premier) + if s.L3L4 == nil && dstIPRaw != 0 { + s.L3L4 = &correlation.L3L4{ + DstIP: tlsDstIP, + DstPort: dstPort, + } } }) counter.Add(1) @@ -825,7 +836,9 @@ func consumeHTTPPlainEvents(ctx context.Context, rd *perf.Reader, mgr *correlati // src_ip et src_port en host byte order (bpf_ntohl appliqué dans tc_capture.c) srcIPRaw := binary.LittleEndian.Uint32(data[4096:4100]) + dstIPRaw := binary.LittleEndian.Uint32(data[4100:4104]) srcPort := binary.LittleEndian.Uint16(data[4104:4106]) + dstPort := binary.LittleEndian.Uint16(data[4106:4108]) if srcIPRaw == 0 && srcPort == 0 { continue @@ -838,6 +851,12 @@ func consumeHTTPPlainEvents(ctx context.Context, rd *perf.Reader, mgr *correlati key.SrcIP[3] = byte(srcIPRaw) key.SrcPort = srcPort + var httpDstIP [4]byte + httpDstIP[0] = byte(dstIPRaw >> 24) + httpDstIP[1] = byte(dstIPRaw >> 16) + httpDstIP[2] = byte(dstIPRaw >> 8) + httpDstIP[3] = byte(dstIPRaw) + // Extraire le payload HTTP if len(data) < 4110 { continue @@ -872,8 +891,13 @@ func consumeHTTPPlainEvents(ctx context.Context, rd *perf.Reader, mgr *correlati HeaderKV: req.HeaderKV, HTTPVersion: req.Protocol, }) - // Corréler si L3/L4 est déjà présent (TCP SYN capturé) - _ = s.L3L4 // corrélation implicite + // Peupler L3/L4 si absent (SYN non capturé) + if s.L3L4 == nil && dstIPRaw != 0 { + s.L3L4 = &correlation.L3L4{ + DstIP: httpDstIP, + DstPort: dstPort, + } + } }) counter.Add(1) } diff --git a/services/ja4ebpf/internal/loader/ja4ssl_x86_bpfel.go b/services/ja4ebpf/internal/loader/ja4ssl_x86_bpfel.go index ba159b8..758ea11 100644 --- a/services/ja4ebpf/internal/loader/ja4ssl_x86_bpfel.go +++ b/services/ja4ebpf/internal/loader/ja4ssl_x86_bpfel.go @@ -61,7 +61,9 @@ type Ja4SslSslReadArgs struct { type Ja4SslTlsHelloEvent struct { Payload [2048]uint8 SrcIp uint32 + DstIp uint32 SrcPort uint16 + DstPort uint16 PayloadLen uint16 TimestampNs uint64 } diff --git a/services/ja4ebpf/internal/loader/ja4tc_x86_bpfel.go b/services/ja4ebpf/internal/loader/ja4tc_x86_bpfel.go index cb549c6..db1556a 100644 --- a/services/ja4ebpf/internal/loader/ja4tc_x86_bpfel.go +++ b/services/ja4ebpf/internal/loader/ja4tc_x86_bpfel.go @@ -61,7 +61,9 @@ type Ja4TcSslReadArgs struct { type Ja4TcTlsHelloEvent struct { Payload [2048]uint8 SrcIp uint32 + DstIp uint32 SrcPort uint16 + DstPort uint16 PayloadLen uint16 TimestampNs uint64 }