mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-07-08 22:03:46 +00:00
Unify Golang and OpenSSL under a single perf event buffer and tls_chunk
struct
This commit is contained in:
parent
bab23303ae
commit
71c4b592e8
@ -11,13 +11,6 @@ Copyright (C) UP9 Inc.
|
|||||||
#include "include/logger_messages.h"
|
#include "include/logger_messages.h"
|
||||||
|
|
||||||
|
|
||||||
struct {
|
|
||||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
|
||||||
__uint(max_entries, 1);
|
|
||||||
__type(key, int);
|
|
||||||
__type(value, struct golang_event);
|
|
||||||
} golang_heap SEC(".maps");
|
|
||||||
|
|
||||||
SEC("uprobe/golang_crypto_tls_write")
|
SEC("uprobe/golang_crypto_tls_write")
|
||||||
static __always_inline int golang_crypto_tls_write_uprobe(struct pt_regs *ctx) {
|
static __always_inline int golang_crypto_tls_write_uprobe(struct pt_regs *ctx) {
|
||||||
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
__u64 pid_tgid = bpf_get_current_pid_tgid();
|
||||||
@ -42,31 +35,31 @@ static __always_inline int golang_crypto_tls_write_uprobe(struct pt_regs *ctx) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct golang_event *event = NULL;
|
struct tls_chunk *chunk = NULL;
|
||||||
int zero = 0;
|
int zero = 0;
|
||||||
|
|
||||||
event = bpf_map_lookup_elem(&golang_heap, &zero);
|
chunk = bpf_map_lookup_elem(&heap, &zero);
|
||||||
|
|
||||||
if (!event) {
|
if (!chunk) {
|
||||||
log_error(ctx, LOG_ERROR_GOLANG_ALLOCATING_EVENT, pid, 0l, 0l);
|
log_error(ctx, LOG_ERROR_GOLANG_ALLOCATING_EVENT, pid, 0l, 0l);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
event->pid = pid;
|
chunk->type = Golang_type;
|
||||||
event->fd = s->fd;
|
chunk->pid = pid;
|
||||||
|
chunk->fd = s->fd;
|
||||||
// ctx->rsi is common between golang_crypto_tls_write_uprobe and golang_crypto_tls_read_uprobe
|
// ctx->rsi is common between golang_crypto_tls_write_uprobe and golang_crypto_tls_read_uprobe
|
||||||
event->conn_addr = ctx->rsi; // go.itab.*net.TCPConn,net.Conn address
|
chunk->flags = ctx->rsi; // go.itab.*net.TCPConn,net.Conn address
|
||||||
event->is_request = true;
|
chunk->is_request = true;
|
||||||
event->len = ctx->rcx;
|
chunk->len = ctx->rcx;
|
||||||
event->cap = ctx->rdi;
|
|
||||||
|
|
||||||
status = bpf_probe_read(&event->data, CHUNK_SIZE, (void*)ctx->rbx);
|
status = bpf_probe_read(&chunk->data, CHUNK_SIZE, (void*)ctx->rbx);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
log_error(ctx, LOG_ERROR_GOLANG_WRITE_READING_DATA, pid_tgid, status, 0l);
|
log_error(ctx, LOG_ERROR_GOLANG_WRITE_READING_DATA, pid_tgid, status, 0l);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bpf_perf_event_output(ctx, &golang_events, BPF_F_CURRENT_CPU, event, sizeof(struct golang_event));
|
bpf_perf_event_output(ctx, &chunks_buffer, BPF_F_CURRENT_CPU, chunk, sizeof(struct tls_chunk));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -88,30 +81,30 @@ static __always_inline int golang_crypto_tls_read_uprobe(struct pt_regs *ctx) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct golang_event *event = NULL;
|
struct tls_chunk *chunk = NULL;
|
||||||
int zero = 0;
|
int zero = 0;
|
||||||
|
|
||||||
event = bpf_map_lookup_elem(&golang_heap, &zero);
|
chunk = bpf_map_lookup_elem(&heap, &zero);
|
||||||
|
|
||||||
if (!event) {
|
if (!chunk) {
|
||||||
log_error(ctx, LOG_ERROR_GOLANG_ALLOCATING_EVENT, pid, 0l, 0l);
|
log_error(ctx, LOG_ERROR_GOLANG_ALLOCATING_EVENT, pid, 0l, 0l);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
event->pid = pid;
|
chunk->type = Golang_type;
|
||||||
|
chunk->pid = pid;
|
||||||
// ctx->rsi is common between golang_crypto_tls_write_uprobe and golang_crypto_tls_read_uprobe
|
// ctx->rsi is common between golang_crypto_tls_write_uprobe and golang_crypto_tls_read_uprobe
|
||||||
event->conn_addr = ctx->rsi; // go.itab.*net.TCPConn,net.Conn address
|
chunk->flags = ctx->rsi; // go.itab.*net.TCPConn,net.Conn address
|
||||||
event->is_request = false;
|
chunk->is_request = false;
|
||||||
event->len = ctx->rcx;
|
chunk->len = ctx->rcx;
|
||||||
event->cap = ctx->rcx; // no cap info
|
|
||||||
|
|
||||||
status = bpf_probe_read(&event->data, CHUNK_SIZE, (void*)(data_p));
|
status = bpf_probe_read(&chunk->data, CHUNK_SIZE, (void*)(data_p));
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
log_error(ctx, LOG_ERROR_GOLANG_READ_READING_DATA, pid_tgid, status, 0l);
|
log_error(ctx, LOG_ERROR_GOLANG_READ_READING_DATA, pid_tgid, status, 0l);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bpf_perf_event_output(ctx, &golang_events, BPF_F_CURRENT_CPU, event, sizeof(struct golang_event));
|
bpf_perf_event_output(ctx, &chunks_buffer, BPF_F_CURRENT_CPU, chunk, sizeof(struct tls_chunk));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,16 @@ Copyright (C) UP9 Inc.
|
|||||||
#define MAX_ENTRIES_LRU_HASH (1 << 14) // 16384
|
#define MAX_ENTRIES_LRU_HASH (1 << 14) // 16384
|
||||||
#define MAX_ENTRIES_RINGBUFF (1 << 24) // 16777216
|
#define MAX_ENTRIES_RINGBUFF (1 << 24) // 16777216
|
||||||
|
|
||||||
|
enum ChunkType {
|
||||||
|
OpenSSL_type=1,
|
||||||
|
Golang_type=2,
|
||||||
|
};
|
||||||
|
|
||||||
// The same struct can be found in chunk.go
|
// The same struct can be found in chunk.go
|
||||||
//
|
//
|
||||||
// Be careful when editing, alignment and padding should be exactly the same in go/c.
|
// Be careful when editing, alignment and padding should be exactly the same in go/c.
|
||||||
//
|
//
|
||||||
struct tlsChunk {
|
struct tls_chunk {
|
||||||
__u32 pid;
|
__u32 pid;
|
||||||
__u32 tgid;
|
__u32 tgid;
|
||||||
__u32 len;
|
__u32 len;
|
||||||
@ -33,6 +38,8 @@ struct tlsChunk {
|
|||||||
__u32 recorded;
|
__u32 recorded;
|
||||||
__u32 fd;
|
__u32 fd;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
|
enum ChunkType type;
|
||||||
|
bool is_request;
|
||||||
__u8 address[16];
|
__u8 address[16];
|
||||||
__u8 data[CHUNK_SIZE]; // Must be N^2
|
__u8 data[CHUNK_SIZE]; // Must be N^2
|
||||||
};
|
};
|
||||||
@ -64,20 +71,20 @@ struct golang_socket {
|
|||||||
__u64 conn_addr;
|
__u64 conn_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct golang_event {
|
|
||||||
__u32 pid;
|
|
||||||
__u32 fd;
|
|
||||||
__u32 conn_addr;
|
|
||||||
bool is_request;
|
|
||||||
__u32 len;
|
|
||||||
__u32 cap;
|
|
||||||
__u8 data[CHUNK_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct golang_event *unused1 __attribute__((unused));
|
const struct golang_event *unused1 __attribute__((unused));
|
||||||
const struct sys_close *unused2 __attribute__((unused));
|
const struct sys_close *unused2 __attribute__((unused));
|
||||||
|
|
||||||
|
|
||||||
|
// Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects.
|
||||||
|
//
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__type(key, int);
|
||||||
|
__type(value, struct tls_chunk);
|
||||||
|
} heap SEC(".maps");
|
||||||
|
|
||||||
|
|
||||||
#define BPF_MAP(_name, _type, _key_type, _value_type, _max_entries) \
|
#define BPF_MAP(_name, _type, _key_type, _value_type, _max_entries) \
|
||||||
struct bpf_map_def SEC("maps") _name = { \
|
struct bpf_map_def SEC("maps") _name = { \
|
||||||
.type = _type, \
|
.type = _type, \
|
||||||
@ -99,16 +106,15 @@ const struct sys_close *unused2 __attribute__((unused));
|
|||||||
BPF_HASH(pids_map, __u32, __u32);
|
BPF_HASH(pids_map, __u32, __u32);
|
||||||
BPF_PERF_OUTPUT(log_buffer);
|
BPF_PERF_OUTPUT(log_buffer);
|
||||||
BPF_PERF_OUTPUT(sys_closes);
|
BPF_PERF_OUTPUT(sys_closes);
|
||||||
|
BPF_PERF_OUTPUT(chunks_buffer);
|
||||||
|
|
||||||
// OpenSSL specific
|
// OpenSSL specific
|
||||||
BPF_LRU_HASH(ssl_write_context, __u64, struct ssl_info);
|
BPF_LRU_HASH(ssl_write_context, __u64, struct ssl_info);
|
||||||
BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info);
|
BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info);
|
||||||
BPF_LRU_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
|
BPF_LRU_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
|
||||||
BPF_PERF_OUTPUT(chunks_buffer);
|
|
||||||
|
|
||||||
// Golang specific
|
// Golang specific
|
||||||
BPF_LRU_HASH(golang_dial_to_socket, __u64, struct golang_socket);
|
BPF_LRU_HASH(golang_dial_to_socket, __u64, struct golang_socket);
|
||||||
BPF_LRU_HASH(golang_socket_to_write, __u64, struct golang_socket);
|
BPF_LRU_HASH(golang_socket_to_write, __u64, struct golang_socket);
|
||||||
BPF_PERF_OUTPUT(golang_events);
|
|
||||||
|
|
||||||
#endif /* __MAPS__ */
|
#endif /* __MAPS__ */
|
||||||
|
@ -11,14 +11,6 @@ Copyright (C) UP9 Inc.
|
|||||||
#include "include/logger_messages.h"
|
#include "include/logger_messages.h"
|
||||||
#include "include/pids.h"
|
#include "include/pids.h"
|
||||||
|
|
||||||
// Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects.
|
|
||||||
//
|
|
||||||
struct {
|
|
||||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
|
||||||
__uint(max_entries, 1);
|
|
||||||
__type(key, int);
|
|
||||||
__type(value, struct tlsChunk);
|
|
||||||
} heap SEC(".maps");
|
|
||||||
|
|
||||||
static __always_inline int get_count_bytes(struct pt_regs *ctx, struct ssl_info* info, __u64 id) {
|
static __always_inline int get_count_bytes(struct pt_regs *ctx, struct ssl_info* info, __u64 id) {
|
||||||
int returnValue = PT_REGS_RC(ctx);
|
int returnValue = PT_REGS_RC(ctx);
|
||||||
@ -48,7 +40,7 @@ static __always_inline int get_count_bytes(struct pt_regs *ctx, struct ssl_info*
|
|||||||
return countBytes;
|
return countBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tlsChunk* chunk, __u64 id, __u32 fd) {
|
static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tls_chunk* chunk, __u64 id, __u32 fd) {
|
||||||
__u32 pid = id >> 32;
|
__u32 pid = id >> 32;
|
||||||
__u64 key = (__u64) pid << 32 | fd;
|
__u64 key = (__u64) pid << 32 | fd;
|
||||||
|
|
||||||
@ -70,7 +62,7 @@ static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tlsC
|
|||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void send_chunk_part(struct pt_regs *ctx, __u8* buffer, __u64 id,
|
static __always_inline void send_chunk_part(struct pt_regs *ctx, __u8* buffer, __u64 id,
|
||||||
struct tlsChunk* chunk, int start, int end) {
|
struct tls_chunk* chunk, int start, int end) {
|
||||||
size_t recorded = MIN(end - start, sizeof(chunk->data));
|
size_t recorded = MIN(end - start, sizeof(chunk->data));
|
||||||
|
|
||||||
if (recorded <= 0) {
|
if (recorded <= 0) {
|
||||||
@ -95,10 +87,10 @@ static __always_inline void send_chunk_part(struct pt_regs *ctx, __u8* buffer, _
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bpf_perf_event_output(ctx, &chunks_buffer, BPF_F_CURRENT_CPU, chunk, sizeof(struct tlsChunk));
|
bpf_perf_event_output(ctx, &chunks_buffer, BPF_F_CURRENT_CPU, chunk, sizeof(struct tls_chunk));
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void send_chunk(struct pt_regs *ctx, __u8* buffer, __u64 id, struct tlsChunk* chunk) {
|
static __always_inline void send_chunk(struct pt_regs *ctx, __u8* buffer, __u64 id, struct tls_chunk* chunk) {
|
||||||
// ebpf loops must be bounded at compile time, we can't use (i < chunk->len / CHUNK_SIZE)
|
// ebpf loops must be bounded at compile time, we can't use (i < chunk->len / CHUNK_SIZE)
|
||||||
//
|
//
|
||||||
// https://lwn.net/Articles/794934/
|
// https://lwn.net/Articles/794934/
|
||||||
@ -127,7 +119,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tlsChunk* chunk;
|
struct tls_chunk* chunk;
|
||||||
int zero = 0;
|
int zero = 0;
|
||||||
|
|
||||||
// If other thread, running on the same CPU get to this point at the same time like us (context switch)
|
// If other thread, running on the same CPU get to this point at the same time like us (context switch)
|
||||||
@ -140,6 +132,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chunk->type = OpenSSL_type;
|
||||||
chunk->flags = flags;
|
chunk->flags = flags;
|
||||||
chunk->pid = id >> 32;
|
chunk->pid = id >> 32;
|
||||||
chunk->tgid = id;
|
chunk->tgid = id;
|
||||||
|
@ -12,23 +12,7 @@ import (
|
|||||||
const FLAGS_IS_CLIENT_BIT uint32 = (1 << 0)
|
const FLAGS_IS_CLIENT_BIT uint32 = (1 << 0)
|
||||||
const FLAGS_IS_READ_BIT uint32 = (1 << 1)
|
const FLAGS_IS_READ_BIT uint32 = (1 << 1)
|
||||||
|
|
||||||
// The same struct can be found in maps.h
|
func (c *tlsTapperTlsChunk) getAddress() (net.IP, uint16, error) {
|
||||||
//
|
|
||||||
// Be careful when editing, alignment and padding should be exactly the same in go/c.
|
|
||||||
//
|
|
||||||
type tlsChunk struct {
|
|
||||||
Pid uint32 // process id
|
|
||||||
Tgid uint32 // thread id inside the process
|
|
||||||
Len uint32 // the size of the native buffer used to read/write the tls data (may be bigger than tlsChunk.Data[])
|
|
||||||
Start uint32 // the start offset withing the native buffer
|
|
||||||
Recorded uint32 // number of bytes copied from the native buffer to tlsChunk.Data[]
|
|
||||||
Fd uint32 // the file descriptor used to read/write the tls data (probably socket file descriptor)
|
|
||||||
Flags uint32 // bitwise flags
|
|
||||||
Address [16]byte // ipv4 address and port
|
|
||||||
Data [4096]byte // actual tls data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tlsChunk) getAddress() (net.IP, uint16, error) {
|
|
||||||
address := bytes.NewReader(c.Address[:])
|
address := bytes.NewReader(c.Address[:])
|
||||||
var family uint16
|
var family uint16
|
||||||
var port uint16
|
var port uint16
|
||||||
@ -51,31 +35,31 @@ func (c *tlsChunk) getAddress() (net.IP, uint16, error) {
|
|||||||
return ip, port, nil
|
return ip, port, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) isClient() bool {
|
func (c *tlsTapperTlsChunk) isClient() bool {
|
||||||
return c.Flags&FLAGS_IS_CLIENT_BIT != 0
|
return c.Flags&FLAGS_IS_CLIENT_BIT != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) isServer() bool {
|
func (c *tlsTapperTlsChunk) isServer() bool {
|
||||||
return !c.isClient()
|
return !c.isClient()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) isRead() bool {
|
func (c *tlsTapperTlsChunk) isRead() bool {
|
||||||
return c.Flags&FLAGS_IS_READ_BIT != 0
|
return c.Flags&FLAGS_IS_READ_BIT != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) isWrite() bool {
|
func (c *tlsTapperTlsChunk) isWrite() bool {
|
||||||
return !c.isRead()
|
return !c.isRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) getRecordedData() []byte {
|
func (c *tlsTapperTlsChunk) getRecordedData() []byte {
|
||||||
return c.Data[:c.Recorded]
|
return c.Data[:c.Recorded]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) isRequest() bool {
|
func (c *tlsTapperTlsChunk) isRequest() bool {
|
||||||
return (c.isClient() && c.isWrite()) || (c.isServer() && c.isRead())
|
return (c.isClient() && c.isWrite()) || (c.isServer() && c.isRead())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsChunk) getAddressPair() (addressPair, error) {
|
func (c *tlsTapperTlsChunk) getAddressPair() (addressPair, error) {
|
||||||
ip, port, err := c.getAddress()
|
ip, port, err := c.getAddress()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -75,12 +74,6 @@ func (p *tlsPoller) init(bpfObjects *tlsTapperObjects, bufferSize int) error {
|
|||||||
return errors.Wrap(err, 0)
|
return errors.Wrap(err, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.golangReader, err = perf.NewReader(bpfObjects.GolangEvents, bufferSize)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sysCloses, err = perf.NewReader(bpfObjects.SysCloses, bufferSize)
|
p.sysCloses, err = perf.NewReader(bpfObjects.SysCloses, bufferSize)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -97,9 +90,11 @@ func (p *tlsPoller) close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) pollSsllib(emitter api.Emitter, options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) {
|
func (p *tlsPoller) pollSsllib(emitter api.Emitter, options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) {
|
||||||
chunks := make(chan *tlsChunk)
|
// tlsTapperTlsChunk is generated by bpf2go.
|
||||||
|
chunks := make(chan *tlsTapperTlsChunk)
|
||||||
|
|
||||||
go p.pollChunksPerfBuffer(chunks)
|
go p.pollChunksPerfBuffer(chunks)
|
||||||
|
go p.pollSysClose(p.sysCloses)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -108,53 +103,32 @@ func (p *tlsPoller) pollSsllib(emitter api.Emitter, options *api.TrafficFilterin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.handleTlsChunk(chunk, p.extension, emitter, options, streamsMap); err != nil {
|
switch chunk.Type {
|
||||||
|
case 1:
|
||||||
|
if err := p.handleOpenSslTlsChunk(chunk, p.extension, emitter, options, streamsMap); err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
|
case 2:
|
||||||
|
if err := p.handleGolangTlsChunk(chunk, emitter, options, streamsMap); err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
case key := <-p.closedReaders:
|
case key := <-p.closedReaders:
|
||||||
delete(p.readers, key)
|
delete(p.readers, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) pollGolang(emitter api.Emitter, options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) {
|
func (p *tlsPoller) handleGolangTlsChunk(chunk *tlsTapperTlsChunk, emitter api.Emitter, options *api.TrafficFilteringOptions,
|
||||||
go p.pollGolangReadWrite(p.golangReader, emitter, options, streamsMap)
|
streamsMap api.TcpStreamMap) error {
|
||||||
go p.pollSysClose(p.sysCloses)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tlsPoller) pollGolangReadWrite(rd *perf.Reader, emitter api.Emitter, options *api.TrafficFilteringOptions,
|
|
||||||
streamsMap api.TcpStreamMap) {
|
|
||||||
nativeEndian := p.getByteOrder()
|
|
||||||
// tlsTapperGolangEvent is generated by bpf2go.
|
|
||||||
var b tlsTapperGolangEvent
|
|
||||||
for {
|
|
||||||
record, err := rd.Read()
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, perf.ErrClosed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Log.Errorf("reading from Golang tls reader: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if record.LostSamples != 0 {
|
|
||||||
logger.Log.Infof("Golang perf event ring buffer full, dropped %d samples", record.LostSamples)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), nativeEndian, &b); err != nil {
|
|
||||||
logger.Log.Errorf("parsing Golang perf event: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.golangConnectionMap.Len()+1 > golangMapLimit {
|
if p.golangConnectionMap.Len()+1 > golangMapLimit {
|
||||||
pair := p.golangConnectionMap.Oldest()
|
pair := p.golangConnectionMap.Oldest()
|
||||||
pair.Value.(*golangConnection).close()
|
pair.Value.(*golangConnection).close()
|
||||||
p.golangConnectionMap.Delete(pair.Key)
|
p.golangConnectionMap.Delete(pair.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pid := uint64(b.Pid)
|
pid := uint64(chunk.Pid)
|
||||||
identifier := pid<<32 + uint64(b.ConnAddr)
|
identifier := pid<<32 + uint64(chunk.Flags)
|
||||||
|
|
||||||
var connection *golangConnection
|
var connection *golangConnection
|
||||||
var _connection interface{}
|
var _connection interface{}
|
||||||
@ -162,23 +136,22 @@ func (p *tlsPoller) pollGolangReadWrite(rd *perf.Reader, emitter api.Emitter, op
|
|||||||
if _connection, ok = p.golangConnectionMap.Get(identifier); !ok {
|
if _connection, ok = p.golangConnectionMap.Get(identifier); !ok {
|
||||||
tlsEmitter := &tlsEmitter{
|
tlsEmitter := &tlsEmitter{
|
||||||
delegate: emitter,
|
delegate: emitter,
|
||||||
namespace: p.getNamespace(b.Pid),
|
namespace: p.getNamespace(chunk.Pid),
|
||||||
}
|
}
|
||||||
|
|
||||||
connection = NewGolangConnection(b.Pid, b.ConnAddr, p.extension, tlsEmitter)
|
connection = NewGolangConnection(chunk.Pid, chunk.Flags, p.extension, tlsEmitter)
|
||||||
p.golangConnectionMap.Set(identifier, connection)
|
p.golangConnectionMap.Set(identifier, connection)
|
||||||
streamsMap.Store(streamsMap.NextId(), connection.stream)
|
streamsMap.Store(streamsMap.NextId(), connection.stream)
|
||||||
} else {
|
} else {
|
||||||
connection = _connection.(*golangConnection)
|
connection = _connection.(*golangConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.IsRequest {
|
if chunk.IsRequest {
|
||||||
connection.fd = b.Fd
|
connection.fd = chunk.Fd
|
||||||
|
|
||||||
err := connection.setAddressBySockfd(p.procfs, b.Pid, b.Fd)
|
err := connection.setAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error resolving address pair from fd: %s", err)
|
return fmt.Errorf("Error resolving address pair from fd: %s", err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpid := p.buildTcpId(&connection.addressPair)
|
tcpid := p.buildTcpId(&connection.addressPair)
|
||||||
@ -193,15 +166,16 @@ func (p *tlsPoller) pollGolangReadWrite(rd *perf.Reader, emitter api.Emitter, op
|
|||||||
go dissect(p.extension, connection.clientReader, options)
|
go dissect(p.extension, connection.clientReader, options)
|
||||||
go dissect(p.extension, connection.serverReader, options)
|
go dissect(p.extension, connection.serverReader, options)
|
||||||
|
|
||||||
request := make([]byte, len(b.Data[:b.Len]))
|
request := make([]byte, len(chunk.Data[:chunk.Len]))
|
||||||
copy(request, b.Data[:b.Len])
|
copy(request, chunk.Data[:chunk.Len])
|
||||||
connection.clientReader.send(request)
|
connection.clientReader.send(request)
|
||||||
} else {
|
} else {
|
||||||
response := make([]byte, len(b.Data[:b.Len]))
|
response := make([]byte, len(chunk.Data[:chunk.Len]))
|
||||||
copy(response, b.Data[:b.Len])
|
copy(response, chunk.Data[:chunk.Len])
|
||||||
connection.serverReader.send(response)
|
connection.serverReader.send(response)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) pollSysClose(rd *perf.Reader) {
|
func (p *tlsPoller) pollSysClose(rd *perf.Reader) {
|
||||||
@ -239,7 +213,7 @@ func (p *tlsPoller) pollSysClose(rd *perf.Reader) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) pollChunksPerfBuffer(chunks chan<- *tlsChunk) {
|
func (p *tlsPoller) pollChunksPerfBuffer(chunks chan<- *tlsTapperTlsChunk) {
|
||||||
logger.Log.Infof("Start polling for tls events")
|
logger.Log.Infof("Start polling for tls events")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -263,7 +237,7 @@ func (p *tlsPoller) pollChunksPerfBuffer(chunks chan<- *tlsChunk) {
|
|||||||
|
|
||||||
buffer := bytes.NewReader(record.RawSample)
|
buffer := bytes.NewReader(record.RawSample)
|
||||||
|
|
||||||
var chunk tlsChunk
|
var chunk tlsTapperTlsChunk
|
||||||
|
|
||||||
if err := binary.Read(buffer, binary.LittleEndian, &chunk); err != nil {
|
if err := binary.Read(buffer, binary.LittleEndian, &chunk); err != nil {
|
||||||
LogError(errors.Errorf("Error parsing chunk %v", err))
|
LogError(errors.Errorf("Error parsing chunk %v", err))
|
||||||
@ -274,7 +248,7 @@ func (p *tlsPoller) pollChunksPerfBuffer(chunks chan<- *tlsChunk) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) handleTlsChunk(chunk *tlsChunk, extension *api.Extension, emitter api.Emitter,
|
func (p *tlsPoller) handleOpenSslTlsChunk(chunk *tlsTapperTlsChunk, extension *api.Extension, emitter api.Emitter,
|
||||||
options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) error {
|
options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) error {
|
||||||
address, err := p.getSockfdAddressPair(chunk)
|
address, err := p.getSockfdAddressPair(chunk)
|
||||||
|
|
||||||
@ -303,7 +277,7 @@ func (p *tlsPoller) handleTlsChunk(chunk *tlsChunk, extension *api.Extension, em
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, address *addressPair, key string,
|
func (p *tlsPoller) startNewTlsReader(chunk *tlsTapperTlsChunk, address *addressPair, key string,
|
||||||
emitter api.Emitter, extension *api.Extension, options *api.TrafficFilteringOptions,
|
emitter api.Emitter, extension *api.Extension, options *api.TrafficFilteringOptions,
|
||||||
streamsMap api.TcpStreamMap) *tlsReader {
|
streamsMap api.TcpStreamMap) *tlsReader {
|
||||||
|
|
||||||
@ -320,7 +294,7 @@ func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, address *addressPair, key
|
|||||||
|
|
||||||
reader := &tlsReader{
|
reader := &tlsReader{
|
||||||
key: key,
|
key: key,
|
||||||
chunks: make(chan *tlsChunk, 1),
|
chunks: make(chan *tlsTapperTlsChunk, 1),
|
||||||
doneHandler: doneHandler,
|
doneHandler: doneHandler,
|
||||||
progress: &api.ReadProgress{},
|
progress: &api.ReadProgress{},
|
||||||
tcpID: &tcpid,
|
tcpID: &tcpid,
|
||||||
@ -358,7 +332,7 @@ func (p *tlsPoller) closeReader(key string, r *tlsReader) {
|
|||||||
p.closedReaders <- key
|
p.closedReaders <- key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) getSockfdAddressPair(chunk *tlsChunk) (addressPair, error) {
|
func (p *tlsPoller) getSockfdAddressPair(chunk *tlsTapperTlsChunk) (addressPair, error) {
|
||||||
address, err := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd)
|
address, err := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd)
|
||||||
fdCacheKey := fmt.Sprintf("%d:%d", chunk.Pid, chunk.Fd)
|
fdCacheKey := fmt.Sprintf("%d:%d", chunk.Pid, chunk.Fd)
|
||||||
|
|
||||||
@ -434,7 +408,7 @@ func (p *tlsPoller) clearPids() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsPoller) logTls(chunk *tlsChunk, key string, reader *tlsReader) {
|
func (p *tlsPoller) logTls(chunk *tlsTapperTlsChunk, key string, reader *tlsReader) {
|
||||||
var flagsStr string
|
var flagsStr string
|
||||||
|
|
||||||
if chunk.isClient() {
|
if chunk.isClient() {
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
type tlsReader struct {
|
type tlsReader struct {
|
||||||
key string
|
key string
|
||||||
chunks chan *tlsChunk
|
chunks chan *tlsTapperTlsChunk
|
||||||
seenChunks int
|
seenChunks int
|
||||||
data []byte
|
data []byte
|
||||||
doneHandler func(r *tlsReader)
|
doneHandler func(r *tlsReader)
|
||||||
@ -24,14 +24,14 @@ type tlsReader struct {
|
|||||||
reqResMatcher api.RequestResponseMatcher
|
reqResMatcher api.RequestResponseMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *tlsReader) newChunk(chunk *tlsChunk) {
|
func (r *tlsReader) newChunk(chunk *tlsTapperTlsChunk) {
|
||||||
r.captureTime = time.Now()
|
r.captureTime = time.Now()
|
||||||
r.seenChunks = r.seenChunks + 1
|
r.seenChunks = r.seenChunks + 1
|
||||||
r.chunks <- chunk
|
r.chunks <- chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *tlsReader) Read(p []byte) (int, error) {
|
func (r *tlsReader) Read(p []byte) (int, error) {
|
||||||
var chunk *tlsChunk
|
var chunk *tlsTapperTlsChunk
|
||||||
|
|
||||||
for len(r.data) == 0 {
|
for len(r.data) == 0 {
|
||||||
var ok bool
|
var ok bool
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
const GLOABL_TAP_PID = 0
|
const GLOABL_TAP_PID = 0
|
||||||
|
|
||||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go@0d0727ef53e2f53b1731c73f4c61e0f58693083a -type golang_event -type sys_close tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86
|
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go@0d0727ef53e2f53b1731c73f4c61e0f58693083a -type tls_chunk -type sys_close tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86
|
||||||
|
|
||||||
type TlsTapper struct {
|
type TlsTapper struct {
|
||||||
bpfObjects tlsTapperObjects
|
bpfObjects tlsTapperObjects
|
||||||
@ -59,7 +59,6 @@ func (t *TlsTapper) Init(chunksBufferSize int, logBufferSize int, procfs string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TlsTapper) Poll(emitter api.Emitter, options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) {
|
func (t *TlsTapper) Poll(emitter api.Emitter, options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) {
|
||||||
t.poller.pollGolang(emitter, options, streamsMap)
|
|
||||||
t.poller.pollSsllib(emitter, options, streamsMap)
|
t.poller.pollSsllib(emitter, options, streamsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,17 +13,6 @@ import (
|
|||||||
"github.com/cilium/ebpf"
|
"github.com/cilium/ebpf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tlsTapperGolangEvent struct {
|
|
||||||
Pid uint32
|
|
||||||
Fd uint32
|
|
||||||
ConnAddr uint32
|
|
||||||
IsRequest bool
|
|
||||||
_ [3]byte
|
|
||||||
Len uint32
|
|
||||||
Cap uint32
|
|
||||||
Data [4096]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type tlsTapperSysClose struct{ Fd uint32 }
|
type tlsTapperSysClose struct{ Fd uint32 }
|
||||||
|
|
||||||
type tlsTapperTlsChunk struct {
|
type tlsTapperTlsChunk struct {
|
||||||
@ -34,8 +23,11 @@ type tlsTapperTlsChunk struct {
|
|||||||
Recorded uint32
|
Recorded uint32
|
||||||
Fd uint32
|
Fd uint32
|
||||||
Flags uint32
|
Flags uint32
|
||||||
|
Type int32
|
||||||
|
IsRequest bool
|
||||||
Address [16]uint8
|
Address [16]uint8
|
||||||
Data [4096]uint8
|
Data [4096]uint8
|
||||||
|
_ [3]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadTlsTapper returns the embedded CollectionSpec for tlsTapper.
|
// loadTlsTapper returns the embedded CollectionSpec for tlsTapper.
|
||||||
@ -109,8 +101,6 @@ type tlsTapperMapSpecs struct {
|
|||||||
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
|
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
|
||||||
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
|
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
|
||||||
GolangDialToSocket *ebpf.MapSpec `ebpf:"golang_dial_to_socket"`
|
GolangDialToSocket *ebpf.MapSpec `ebpf:"golang_dial_to_socket"`
|
||||||
GolangEvents *ebpf.MapSpec `ebpf:"golang_events"`
|
|
||||||
GolangHeap *ebpf.MapSpec `ebpf:"golang_heap"`
|
|
||||||
GolangSocketToWrite *ebpf.MapSpec `ebpf:"golang_socket_to_write"`
|
GolangSocketToWrite *ebpf.MapSpec `ebpf:"golang_socket_to_write"`
|
||||||
Heap *ebpf.MapSpec `ebpf:"heap"`
|
Heap *ebpf.MapSpec `ebpf:"heap"`
|
||||||
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
|
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
|
||||||
@ -144,8 +134,6 @@ type tlsTapperMaps struct {
|
|||||||
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
|
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
|
||||||
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
|
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
|
||||||
GolangDialToSocket *ebpf.Map `ebpf:"golang_dial_to_socket"`
|
GolangDialToSocket *ebpf.Map `ebpf:"golang_dial_to_socket"`
|
||||||
GolangEvents *ebpf.Map `ebpf:"golang_events"`
|
|
||||||
GolangHeap *ebpf.Map `ebpf:"golang_heap"`
|
|
||||||
GolangSocketToWrite *ebpf.Map `ebpf:"golang_socket_to_write"`
|
GolangSocketToWrite *ebpf.Map `ebpf:"golang_socket_to_write"`
|
||||||
Heap *ebpf.Map `ebpf:"heap"`
|
Heap *ebpf.Map `ebpf:"heap"`
|
||||||
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
|
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
|
||||||
@ -162,8 +150,6 @@ func (m *tlsTapperMaps) Close() error {
|
|||||||
m.ConnectSyscallInfo,
|
m.ConnectSyscallInfo,
|
||||||
m.FileDescriptorToIpv4,
|
m.FileDescriptorToIpv4,
|
||||||
m.GolangDialToSocket,
|
m.GolangDialToSocket,
|
||||||
m.GolangEvents,
|
|
||||||
m.GolangHeap,
|
|
||||||
m.GolangSocketToWrite,
|
m.GolangSocketToWrite,
|
||||||
m.Heap,
|
m.Heap,
|
||||||
m.LogBuffer,
|
m.LogBuffer,
|
||||||
|
Binary file not shown.
@ -13,17 +13,6 @@ import (
|
|||||||
"github.com/cilium/ebpf"
|
"github.com/cilium/ebpf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tlsTapperGolangEvent struct {
|
|
||||||
Pid uint32
|
|
||||||
Fd uint32
|
|
||||||
ConnAddr uint32
|
|
||||||
IsRequest bool
|
|
||||||
_ [3]byte
|
|
||||||
Len uint32
|
|
||||||
Cap uint32
|
|
||||||
Data [4096]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type tlsTapperSysClose struct{ Fd uint32 }
|
type tlsTapperSysClose struct{ Fd uint32 }
|
||||||
|
|
||||||
type tlsTapperTlsChunk struct {
|
type tlsTapperTlsChunk struct {
|
||||||
@ -34,8 +23,11 @@ type tlsTapperTlsChunk struct {
|
|||||||
Recorded uint32
|
Recorded uint32
|
||||||
Fd uint32
|
Fd uint32
|
||||||
Flags uint32
|
Flags uint32
|
||||||
|
Type int32
|
||||||
|
IsRequest bool
|
||||||
Address [16]uint8
|
Address [16]uint8
|
||||||
Data [4096]uint8
|
Data [4096]uint8
|
||||||
|
_ [3]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadTlsTapper returns the embedded CollectionSpec for tlsTapper.
|
// loadTlsTapper returns the embedded CollectionSpec for tlsTapper.
|
||||||
@ -109,8 +101,6 @@ type tlsTapperMapSpecs struct {
|
|||||||
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
|
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
|
||||||
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
|
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
|
||||||
GolangDialToSocket *ebpf.MapSpec `ebpf:"golang_dial_to_socket"`
|
GolangDialToSocket *ebpf.MapSpec `ebpf:"golang_dial_to_socket"`
|
||||||
GolangEvents *ebpf.MapSpec `ebpf:"golang_events"`
|
|
||||||
GolangHeap *ebpf.MapSpec `ebpf:"golang_heap"`
|
|
||||||
GolangSocketToWrite *ebpf.MapSpec `ebpf:"golang_socket_to_write"`
|
GolangSocketToWrite *ebpf.MapSpec `ebpf:"golang_socket_to_write"`
|
||||||
Heap *ebpf.MapSpec `ebpf:"heap"`
|
Heap *ebpf.MapSpec `ebpf:"heap"`
|
||||||
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
|
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
|
||||||
@ -144,8 +134,6 @@ type tlsTapperMaps struct {
|
|||||||
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
|
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
|
||||||
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
|
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
|
||||||
GolangDialToSocket *ebpf.Map `ebpf:"golang_dial_to_socket"`
|
GolangDialToSocket *ebpf.Map `ebpf:"golang_dial_to_socket"`
|
||||||
GolangEvents *ebpf.Map `ebpf:"golang_events"`
|
|
||||||
GolangHeap *ebpf.Map `ebpf:"golang_heap"`
|
|
||||||
GolangSocketToWrite *ebpf.Map `ebpf:"golang_socket_to_write"`
|
GolangSocketToWrite *ebpf.Map `ebpf:"golang_socket_to_write"`
|
||||||
Heap *ebpf.Map `ebpf:"heap"`
|
Heap *ebpf.Map `ebpf:"heap"`
|
||||||
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
|
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
|
||||||
@ -162,8 +150,6 @@ func (m *tlsTapperMaps) Close() error {
|
|||||||
m.ConnectSyscallInfo,
|
m.ConnectSyscallInfo,
|
||||||
m.FileDescriptorToIpv4,
|
m.FileDescriptorToIpv4,
|
||||||
m.GolangDialToSocket,
|
m.GolangDialToSocket,
|
||||||
m.GolangEvents,
|
|
||||||
m.GolangHeap,
|
|
||||||
m.GolangSocketToWrite,
|
m.GolangSocketToWrite,
|
||||||
m.Heap,
|
m.Heap,
|
||||||
m.LogBuffer,
|
m.LogBuffer,
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user