Selaa lähdekoodia

eBPF-based TLS connections tracing for Golang applications

Anton Petruhin 2 vuotta sitten
vanhempi
säilyke
2cf13850e2

+ 2 - 2
Dockerfile

@@ -1,5 +1,5 @@
 FROM golang:1.19-bullseye AS builder
-RUN apt-get update && apt-get install -y libsystemd-dev
+RUN apt update && apt install -y libsystemd-dev
 COPY go.mod /tmp/src/
 COPY go.sum /tmp/src/
 WORKDIR /tmp/src/
@@ -10,6 +10,6 @@ ARG VERSION=unknown
 RUN CGO_ENABLED=1 go install -mod=readonly -ldflags "-X main.version=$VERSION" /tmp/src
 
 FROM debian:bullseye
-RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
+RUN apt update && apt install -y ca-certificates && apt clean
 COPY --from=builder /go/bin/coroot-node-agent /usr/bin/coroot-node-agent
 ENTRYPOINT ["coroot-node-agent"]

+ 9 - 2
containers/container.go

@@ -1,6 +1,7 @@
 package containers
 
 import (
+	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/cgroup"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/ebpftracer"
@@ -89,6 +90,7 @@ type Process struct {
 	Pid       uint32
 	StartedAt time.Time
 	NetNsId   string
+	uprobes   []link.Link
 }
 
 func (p *Process) isHostNs() bool {
@@ -356,7 +358,7 @@ func (c *Container) Collect(ch chan<- prometheus.Metric) {
 	}
 }
 
-func (c *Container) onProcessStart(pid uint32) {
+func (c *Container) onProcessStart(pid uint32, uprobes []link.Link) {
 	c.lock.Lock()
 	defer c.lock.Unlock()
 	stats, err := TaskstatsPID(pid)
@@ -369,7 +371,7 @@ func (c *Container) onProcessStart(pid uint32) {
 	}
 	defer ns.Close()
 	c.zombieAt = time.Time{}
-	c.processes[pid] = &Process{Pid: pid, StartedAt: stats.BeginTime, NetNsId: ns.UniqueId()}
+	c.processes[pid] = &Process{Pid: pid, StartedAt: stats.BeginTime, NetNsId: ns.UniqueId(), uprobes: uprobes}
 
 	if c.startedAt.IsZero() {
 		c.startedAt = stats.BeginTime
@@ -390,6 +392,11 @@ func (c *Container) onProcessStart(pid uint32) {
 func (c *Container) onProcessExit(pid uint32, oomKill bool) {
 	c.lock.Lock()
 	defer c.lock.Unlock()
+	if p := c.processes[pid]; p != nil {
+		for _, u := range p.uprobes {
+			_ = u.Close()
+		}
+	}
 	delete(c.processes, pid)
 	if len(c.processes) == 0 {
 		c.zombieAt = time.Now()

+ 9 - 8
containers/registry.go

@@ -79,7 +79,7 @@ func NewRegistry(reg prometheus.Registerer, kernelVersion string) (*Registry, er
 		return nil, err
 	}
 
-	cs := &Registry{
+	r := &Registry{
 		reg:    reg,
 		events: make(chan ebpftracer.Event, 10000),
 
@@ -88,17 +88,17 @@ func NewRegistry(reg prometheus.Registerer, kernelVersion string) (*Registry, er
 		containersById:       map[ContainerID]*Container{},
 		containersByCgroupId: map[string]*Container{},
 		containersByPid:      map[uint32]*Container{},
+
+		tracer: ebpftracer.NewTracer(kernelVersion, *flags.DisableL7Tracing),
 	}
 
-	go cs.handleEvents(cs.events)
-	t, err := ebpftracer.NewTracer(cs.events, kernelVersion, *flags.DisableL7Tracing)
-	if err != nil {
-		close(cs.events)
+	go r.handleEvents(r.events)
+	if err = r.tracer.Run(r.events); err != nil {
+		close(r.events)
 		return nil, err
 	}
-	cs.tracer = t
 
-	return cs, nil
+	return r, nil
 }
 
 func (r *Registry) Close() {
@@ -167,7 +167,8 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					}
 				}
 				if c := r.getOrCreateContainer(e.Pid); c != nil {
-					c.onProcessStart(e.Pid)
+					uprobes := r.tracer.AttachGoTlsUprobes(e.Pid)
+					c.onProcessStart(e.Pid, uprobes)
 				}
 			case ebpftracer.EventTypeProcessExit:
 				if c := r.containersByPid[e.Pid]; c != nil {

+ 21 - 9
ebpftracer/Dockerfile

@@ -5,14 +5,26 @@ RUN apk add llvm clang libbpf-dev linux-headers
 COPY ebpf /tmp/ebpf
 WORKDIR /tmp/ebpf
 
-RUN clang -g -O2 -target bpf -D__KERNEL=416 -c ebpf.c -o ebpf416.o && llvm-strip --strip-debug ebpf416.o
-RUN clang -g -O2 -target bpf -D__KERNEL=420 -c ebpf.c -o ebpf420.o && llvm-strip --strip-debug ebpf420.o
-RUN clang -g -O2 -target bpf -D__KERNEL=506 -c ebpf.c -o ebpf506.o && llvm-strip --strip-debug ebpf506.o
-RUN clang -g -O2 -target bpf -D__KERNEL=512 -c ebpf.c -o ebpf512.o && llvm-strip --strip-debug ebpf512.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=416 -D__TARGET_ARCH_x86 -c ebpf.c -o ebpf416x86.o && llvm-strip --strip-debug ebpf416x86.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=420 -D__TARGET_ARCH_x86 -c ebpf.c -o ebpf420x86.o && llvm-strip --strip-debug ebpf420x86.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=506 -D__TARGET_ARCH_x86 -c ebpf.c -o ebpf506x86.o && llvm-strip --strip-debug ebpf506x86.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=512 -D__TARGET_ARCH_x86 -c ebpf.c -o ebpf512x86.o && llvm-strip --strip-debug ebpf512x86.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=416 -D__TARGET_ARCH_arm64 -c ebpf.c -o ebpf416arm64.o && llvm-strip --strip-debug ebpf416arm64.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=420 -D__TARGET_ARCH_arm64 -c ebpf.c -o ebpf420arm64.o && llvm-strip --strip-debug ebpf420arm64.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=506 -D__TARGET_ARCH_arm64 -c ebpf.c -o ebpf506arm64.o && llvm-strip --strip-debug ebpf506arm64.o
+RUN clang -g -O2 -target bpf -D__KERNEL_FROM=512 -D__TARGET_ARCH_arm64 -c ebpf.c -o ebpf512arm64.o && llvm-strip --strip-debug ebpf512arm64.o
 
-RUN echo -en '// generated - do not edit\npackage ebpftracer\nvar ebpfProg = []struct{v string; p []byte}{\n' > ebpf.go \
-	&& echo -en '\t{"v5.12", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf512.o >> ebpf.go && echo '")},' >> ebpf.go \
-	&& echo -en '\t{"v5.6", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf506.o >> ebpf.go && echo '")},' >> ebpf.go \
-	&& echo -en '\t{"v4.20", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf420.o >> ebpf.go && echo '")},' >> ebpf.go \
-	&& echo -en '\t{"v4.16", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf416.o >> ebpf.go && echo '")},' >> ebpf.go \
+RUN echo -en '// generated - do not edit\npackage ebpftracer\nvar ebpfProg = map[string][]struct{v string; p []byte}{\n' > ebpf.go \
+	&& echo -en '\t"amd64": {\n' >> ebpf.go \
+	&& echo -en '\t\t{"v5.12", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf512x86.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t\t{"v5.6", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf506x86.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t\t{"v4.20", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf420x86.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t\t{"v4.16", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf416x86.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t},\n'>> ebpf.go \
+	&& echo -en '\t"arm64": {\n' >> ebpf.go \
+	&& echo -en '\t\t{"v5.12", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf512arm64.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t\t{"v5.6", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf506arm64.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t\t{"v4.20", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf420arm64.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t\t{"v4.16", []byte("' >> ebpf.go && hexdump -v -e '"\x" 1/1 "%02x"' ebpf416arm64.o >> ebpf.go && echo '")},' >> ebpf.go \
+	&& echo -en '\t},\n'>> ebpf.go \
 	&& echo -en '}\n'>> ebpf.go

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
ebpftracer/ebpf.go


+ 2 - 1
ebpftracer/ebpf/ebpf.c

@@ -1,4 +1,5 @@
 #include <uapi/linux/bpf.h>
+#include "vmlinux.h"
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_core_read.h>
 #include <bpf/bpf_tracing.h>
@@ -29,11 +30,11 @@
     bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
 })
 
-
 #include "proc.c"
 #include "file.c"
 #include "tcp/state.c"
 #include "tcp/retransmit.c"
 #include "l7/l7.c"
+#include "l7/tls.c"
 
 char _license[] SEC("license") = "GPL";

+ 2 - 0
ebpftracer/ebpf/file.c

@@ -75,6 +75,7 @@ int trace_exit(struct trace_event_raw_sys_exit__stub* ctx)
 	return 0;
 }
 
+#if defined(__TARGET_ARCH_x86)
 SEC("tracepoint/syscalls/sys_enter_open")
 int sys_enter_open(struct trace_event_raw_sys_enter__stub* ctx)
 {
@@ -86,6 +87,7 @@ int sys_exit_open(struct trace_event_raw_sys_exit__stub* ctx)
 {
 	return trace_exit(ctx);
 }
+#endif
 
 SEC("tracepoint/syscalls/sys_enter_openat")
 int sys_enter_openat(struct trace_event_raw_sys_enter__stub* ctx)

+ 46 - 45
ebpftracer/ebpf/l7/l7.c

@@ -53,22 +53,22 @@ struct {
     __uint(value_size, sizeof(int));
 } l7_events SEC(".maps");
 
-struct rw_args_t {
+struct read_args {
     __u64 fd;
     char* buf;
-    __u64 size;
 };
 
 struct {
     __uint(type, BPF_MAP_TYPE_HASH);
     __uint(key_size, sizeof(__u64));
-    __uint(value_size, sizeof(struct rw_args_t));
+    __uint(value_size, sizeof(struct read_args));
     __uint(max_entries, 10240);
 } active_reads SEC(".maps");
 
 struct socket_key {
     __u64 fd;
     __u32 pid;
+    __u16 is_tls;
     __s16 stream_id;
 };
 
@@ -109,7 +109,7 @@ struct trace_event_raw_sys_exit_rw__stub {
     long int ret;
 };
 
-struct iov {
+struct iovec {
     char* buf;
     __u64 size;
 };
@@ -127,7 +127,7 @@ __u64 get_connection_timestamp(__u32 pid, __u64 fd) {
 }
 
 static inline __attribute__((__always_inline__))
-int trace_enter_write(struct trace_event_raw_sys_enter_rw__stub* ctx, __u64 fd, char *buf, __u64 size) {
+int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size) {
     __u64 id = bpf_get_current_pid_tgid();
     int zero = 0;
     struct l7_request *req = bpf_map_lookup_elem(&l7_request_heap, &zero);
@@ -141,6 +141,7 @@ int trace_enter_write(struct trace_event_raw_sys_enter_rw__stub* ctx, __u64 fd,
     struct socket_key k = {};
     k.pid = id >> 32;
     k.fd = fd;
+    k.is_tls = is_tls;
     k.stream_id = -1;
 
     if (is_http_request(buf)) {
@@ -228,37 +229,33 @@ int trace_enter_write(struct trace_event_raw_sys_enter_rw__stub* ctx, __u64 fd,
 }
 
 static inline __attribute__((__always_inline__))
-int trace_enter_read(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    __u64 id = bpf_get_current_pid_tgid();
-    struct rw_args_t args = {};
-    args.fd = ctx->fd;
-    args.buf = ctx->buf;
-    args.size = ctx->size;
+int trace_enter_read(__u64 id, __u64 fd, char *buf) {
+    struct read_args args = {};
+    args.fd = fd;
+    args.buf = buf;
     bpf_map_update_elem(&active_reads, &id, &args, BPF_ANY);
     return 0;
 }
 
 static inline __attribute__((__always_inline__))
-int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
-    __u64 id = bpf_get_current_pid_tgid();
-    int zero = 0;
-    struct rw_args_t *args = bpf_map_lookup_elem(&active_reads, &id);
+int trace_exit_read(void *ctx, __u64 id, __u32 pid, __u16 is_tls, long int ret) {
+    struct read_args *args = bpf_map_lookup_elem(&active_reads, &id);
     if (!args) {
         return 0;
     }
     struct socket_key k = {};
-    k.pid = id >> 32;
+    k.pid = pid;
     k.fd = args->fd;
+    k.is_tls = is_tls;
     k.stream_id = -1;
-    char *buf = args->buf;
-    __u64 size = args->size;
 
     bpf_map_delete_elem(&active_reads, &id);
 
-    if (ctx->ret <= 0) {
+    if (ret <= 0) {
         return 0;
     }
 
+    int zero = 0;
     struct l7_event *e = bpf_map_lookup_elem(&l7_event_heap, &zero);
     if (!e) {
         return 0;
@@ -270,7 +267,7 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     e->method = METHOD_UNKNOWN;
     e->statement_id = 0;
 
-    if (is_rabbitmq_consume(buf, size)) {
+    if (is_rabbitmq_consume(args->buf, ret)) {
         e->protocol = PROTOCOL_RABBITMQ;
         e->status = 200;
         e->method = METHOD_CONSUME;
@@ -283,7 +280,7 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     cassandra_response.stream_id = -1;
     struct l7_request *req = bpf_map_lookup_elem(&active_l7_requests, &k);
     if (!req) {
-        if (bpf_probe_read(&cassandra_response, sizeof(cassandra_response), (void *)(buf)) < 0) {
+        if (bpf_probe_read(&cassandra_response, sizeof(cassandra_response), (void *)(args->buf)) < 0) {
             return 0;
         }
         k.stream_id = cassandra_response.stream_id;
@@ -301,23 +298,23 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
     __u8 request_type = req->request_type;
     bpf_map_delete_elem(&active_l7_requests, &k);
     if (e->protocol == PROTOCOL_HTTP) {
-        e->status = parse_http_status(buf);
+        e->status = parse_http_status(args->buf);
     } else if (e->protocol == PROTOCOL_POSTGRES) {
-        e->status = parse_postgres_status(buf, ctx->ret);
+        e->status = parse_postgres_status(args->buf, ret);
         if (request_type == POSTGRES_FRAME_PARSE) {
             e->method = METHOD_STATEMENT_PREPARE;
         }
     } else if (e->protocol == PROTOCOL_REDIS) {
-        e->status = parse_redis_status(buf, ctx->ret);
+        e->status = parse_redis_status(args->buf, ret);
     } else if (e->protocol == PROTOCOL_MEMCACHED) {
-        e->status = parse_memcached_status(buf, ctx->ret);
+        e->status = parse_memcached_status(args->buf, ret);
     } else if (e->protocol == PROTOCOL_MYSQL) {
-        e->status = parse_mysql_response(buf, ctx->ret, request_type, &e->statement_id);
+        e->status = parse_mysql_response(args->buf, ret, request_type, &e->statement_id);
         if (request_type == MYSQL_COM_STMT_PREPARE) {
             e->method = METHOD_STATEMENT_PREPARE;
         }
     } else if (e->protocol == PROTOCOL_MONGO) {
-        e->status = parse_mongo_status(buf, ctx->ret, partial);
+        e->status = parse_mongo_status(args->buf, ret, partial);
         if (e->status == 1) {
             struct l7_request *r = bpf_map_lookup_elem(&l7_request_heap, &zero);
             if (!r) {
@@ -331,7 +328,7 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
             return 0;
         }
     } else if (e->protocol == PROTOCOL_KAFKA) {
-        e->status = parse_kafka_status(request_id, buf, ctx->ret, partial);
+        e->status = parse_kafka_status(request_id, args->buf, ret, partial);
     } else if (e->protocol == PROTOCOL_CASSANDRA) {
         e->status = cassandra_status(cassandra_response);
     }
@@ -346,58 +343,62 @@ int trace_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
 
 SEC("tracepoint/syscalls/sys_enter_writev")
 int sys_enter_writev(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    struct iov iov0 = {};
-    if (bpf_probe_read(&iov0, sizeof(struct iov), (void *)ctx->buf) < 0) {
+    struct iovec iovec0 = {};
+    if (bpf_probe_read(&iovec0, sizeof(struct iovec), (void *)ctx->buf) < 0) {
         return 0;
     }
-    return trace_enter_write(ctx, ctx->fd, iov0.buf, iov0.size);
+    return trace_enter_write(ctx, ctx->fd, 0, iovec0.buf, iovec0.size);
 }
 
 SEC("tracepoint/syscalls/sys_enter_write")
 int sys_enter_write(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    return trace_enter_write(ctx, ctx->fd, ctx->buf, ctx->size);
+    return trace_enter_write(ctx, ctx->fd, 0, ctx->buf, ctx->size);
 }
 
 SEC("tracepoint/syscalls/sys_enter_sendto")
 int sys_enter_sendto(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    return trace_enter_write(ctx, ctx->fd, ctx->buf, ctx->size);
+    return trace_enter_write(ctx, ctx->fd, 0, ctx->buf, ctx->size);
 }
 
 SEC("tracepoint/syscalls/sys_enter_read")
 int sys_enter_read(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    return trace_enter_read(ctx);
+    __u64 id = bpf_get_current_pid_tgid();
+    return trace_enter_read(id, ctx->fd, ctx->buf);
 }
 
 SEC("tracepoint/syscalls/sys_enter_readv")
 int sys_enter_readv(struct trace_event_raw_sys_enter_rw__stub* ctx) {
     __u64 id = bpf_get_current_pid_tgid();
-    void *vec;
-    if (bpf_probe_read(&vec, sizeof(void*), (void *)ctx->buf) < 0) {
+    struct iovec iovec0 = {};
+    if (bpf_probe_read(&iovec0, sizeof(struct iovec), (void *)ctx->buf) < 0) {
         return 0;
     }
-    struct rw_args_t args = {};
-    args.fd = ctx->fd;
-    args.buf = vec;
-    bpf_map_update_elem(&active_reads, &id, &args, BPF_ANY);
-    return 0;
+    return trace_enter_read(id, ctx->fd, iovec0.buf);
 }
 
 SEC("tracepoint/syscalls/sys_enter_recvfrom")
 int sys_enter_recvfrom(struct trace_event_raw_sys_enter_rw__stub* ctx) {
-    return trace_enter_read(ctx);
+    __u64 id = bpf_get_current_pid_tgid();
+    return trace_enter_read(id, ctx->fd, ctx->buf);
 }
 
 SEC("tracepoint/syscalls/sys_exit_read")
 int sys_exit_read(struct trace_event_raw_sys_exit_rw__stub* ctx) {
-    return trace_exit_read(ctx);
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 pid = pid_tgid >> 32;
+    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
 }
 
 SEC("tracepoint/syscalls/sys_exit_readv")
 int sys_exit_readv(struct trace_event_raw_sys_exit_rw__stub* ctx) {
-    return trace_exit_read(ctx);
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 pid = pid_tgid >> 32;
+    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
 }
 
 SEC("tracepoint/syscalls/sys_exit_recvfrom")
 int sys_exit_recvfrom(struct trace_event_raw_sys_exit_rw__stub* ctx) {
-    return trace_exit_read(ctx);
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u32 pid = pid_tgid >> 32;
+    return trace_exit_read(ctx, pid_tgid, pid, 0, ctx->ret);
 }

+ 70 - 0
ebpftracer/ebpf/l7/tls.c

@@ -0,0 +1,70 @@
+// Go internal ABI specification: https://go.dev/s/regabi
+#if defined(__TARGET_ARCH_x86)
+#define GO_PARAM1(x) ((x)->ax)
+#define GO_PARAM2(x) ((x)->bx)
+#define GO_PARAM3(x) ((x)->cx)
+#define GOROUTINE(x) ((x)->r14)
+#elif defined(__TARGET_ARCH_arm64)
+#define GO_PARAM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])
+#define GO_PARAM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])
+#define GO_PARAM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])
+#define GOROUTINE(x) (((PT_REGS_ARM64 *)(x))->regs[28])
+#endif
+
+#define IS_TLS_READ_ID 0x8000000000000000
+
+struct go_interface {
+    __s64 type;
+    void* ptr;
+};
+
+static inline __attribute__((__always_inline__))
+int go_crypto_tls_get_fd_from_conn(struct pt_regs *ctx, __u32 *fd) {
+    struct go_interface conn;
+    if (bpf_probe_read(&conn, sizeof(conn), (void*)GO_PARAM1(ctx))) {
+        return 1;
+    };
+    void* fd_ptr;
+    if (bpf_probe_read(&fd_ptr, sizeof(fd_ptr), conn.ptr)) {
+        return 1;
+    }
+    if (bpf_probe_read(fd, sizeof(*fd), fd_ptr + 0x10)) {
+        return 1;
+    }
+    return 0;
+}
+
+SEC("uprobe/go_crypto_tls_write_enter")
+int go_crypto_tls_write_enter(struct pt_regs *ctx) {
+    __u32 fd;
+    if (go_crypto_tls_get_fd_from_conn(ctx, &fd)) {
+        return 0;
+    }
+    char *buf_ptr = (char*)GO_PARAM2(ctx);
+    __u64 buf_size = GO_PARAM3(ctx);
+    return trace_enter_write(ctx, fd, 1, buf_ptr, buf_size);
+}
+
+SEC("uprobe/go_crypto_tls_read_enter")
+int go_crypto_tls_read_enter(struct pt_regs *ctx) {
+    __u32 fd;
+    if (go_crypto_tls_get_fd_from_conn(ctx, &fd)) {
+        return 0;
+    }
+    char *buf_ptr = (char*)GO_PARAM2(ctx);
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u64 goroutine_id = GOROUTINE(ctx);
+    __u64 pid = pid_tgid >> 32;
+    __u64 id = pid << 32 | goroutine_id | IS_TLS_READ_ID;
+    return trace_enter_read(id, fd, buf_ptr);
+}
+
+SEC("uprobe/go_crypto_tls_read_exit")
+int go_crypto_tls_read_exit(struct pt_regs *ctx) {
+    __u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u64 pid = pid_tgid >> 32;
+    __u64 goroutine_id = GOROUTINE(ctx);
+    __u64 id = pid << 32 | goroutine_id | IS_TLS_READ_ID;
+    long int ret = GO_PARAM1(ctx);
+    return trace_exit_read(ctx, id, pid, 1, ret);
+}

+ 2 - 2
ebpftracer/ebpf/tcp/retransmit.c

@@ -8,12 +8,12 @@ struct trace_event_raw_tcp_event_sk_skb__stub {
     __u64 unused;
     void *sbkaddr;
     void *skaddr;
-#if __KERNEL >= 420
+#if __KERNEL_FROM >= 420
     int state;
 #endif
     __u16 sport;
     __u16 dport;
-#if __KERNEL >= 512
+#if __KERNEL_FROM >= 512
     __u16 family;
 #endif
     __u8 saddr[4];

+ 1 - 1
ebpftracer/ebpf/tcp/state.c

@@ -31,7 +31,7 @@ struct trace_event_raw_inet_sock_set_state__stub {
     __u16 sport;
     __u16 dport;
     __u16 family;
-#if __KERNEL >= 506
+#if __KERNEL_FROM >= 506
     __u16 protocol;
 #else
     __u8 protocol;

+ 56 - 0
ebpftracer/ebpf/vmlinux.h

@@ -0,0 +1,56 @@
+#ifndef __VMLINUX_H__
+#define __VMLINUX_H__
+
+#if defined(__TARGET_ARCH_x86)
+struct pt_regs {
+	long unsigned int r15;
+	long unsigned int r14;
+	long unsigned int r13;
+	long unsigned int r12;
+	long unsigned int bp;
+	long unsigned int bx;
+	long unsigned int r11;
+	long unsigned int r10;
+	long unsigned int r9;
+	long unsigned int r8;
+	long unsigned int ax;
+	long unsigned int cx;
+	long unsigned int dx;
+	long unsigned int si;
+	long unsigned int di;
+	long unsigned int orig_ax;
+	long unsigned int ip;
+	long unsigned int cs;
+	long unsigned int flags;
+	long unsigned int sp;
+	long unsigned int ss;
+};
+#elif defined(__TARGET_ARCH_arm64)
+struct user_pt_regs {
+	__u64 regs[31];
+	__u64 sp;
+	__u64 pc;
+	__u64 pstate;
+};
+struct pt_regs {
+	union {
+		struct user_pt_regs user_regs;
+		struct {
+			__u64 regs[31];
+			__u64 sp;
+			__u64 pc;
+			__u64 pstate;
+		};
+	};
+	__u64 orig_x0;
+	__s32 syscallno;
+	__u32 unused2;
+	__u64 orig_addr_limit;
+	__u64 pmr_save;
+	__u64 stackframe[2];
+	__u64 lockdep_hardirqs;
+	__u64 exit_rcu;
+};
+#endif
+
+#endif /* __VMLINUX_H__ */

+ 182 - 0
ebpftracer/tls.go

@@ -0,0 +1,182 @@
+package ebpftracer
+
+import (
+	"debug/buildinfo"
+	"debug/elf"
+	"errors"
+	"fmt"
+	"github.com/cilium/ebpf/link"
+	"github.com/coroot/coroot-node-agent/proc"
+	"golang.org/x/arch/arm64/arm64asm"
+	"golang.org/x/arch/x86/x86asm"
+	"golang.org/x/mod/semver"
+	"k8s.io/klog/v2"
+	"os"
+	"strings"
+)
+
+const (
+	minSupportedGoVersion = "v1.17.0"
+	writeSymbol           = "crypto/tls.(*Conn).Write"
+	readSymbol            = "crypto/tls.(*Conn).Read"
+)
+
+func (t *Tracer) AttachGoTlsUprobes(pid uint32) []link.Link {
+	if t.disableL7Tracing {
+		return nil
+	}
+
+	path := proc.Path(pid, "exe")
+
+	var err error
+	var name, version string
+	log := func(msg string, err error) {
+		if err != nil {
+			for _, s := range []string{"not a Go executable", "no such file or directory", "no such process", "permission denied"} {
+				if strings.HasSuffix(err.Error(), s) {
+					return
+				}
+			}
+			klog.ErrorfDepth(1, "pid=%d golang_app=%s golang_version=%s: %s: %s", pid, name, version, msg, err)
+		}
+		klog.InfofDepth(1, "pid=%d golang_app=%s golang_version=%s: %s", pid, name, version, msg)
+	}
+
+	bi, err := buildinfo.ReadFile(path)
+	if err != nil {
+		log("failed to read build info", err)
+		return nil
+	}
+
+	name, err = os.Readlink(path)
+	if err != nil {
+		log("failed to read name", err)
+		return nil
+	}
+	version = strings.Replace(bi.GoVersion, "go", "v", 1)
+	if semver.Compare(version, minSupportedGoVersion) < 0 {
+		log(fmt.Sprintf("go_versions below %s are not supported", minSupportedGoVersion), nil)
+		return nil
+	}
+
+	ef, err := elf.Open(path)
+	if err != nil {
+		log("failed to open as elf binary", err)
+		return nil
+	}
+	defer ef.Close()
+
+	symbols, err := ef.Symbols()
+	if err != nil {
+		if errors.Is(err, elf.ErrNoSymbols) {
+			log("no symbol section", nil)
+			return nil
+		}
+		log("failed to read symbols", err)
+		return nil
+	}
+
+	textSection := ef.Section(".text")
+	if textSection == nil {
+		log("no text section", nil)
+		return nil
+	}
+	textSectionData, err := textSection.Data()
+	if err != nil {
+		log("failed to read text section", err)
+		return nil
+	}
+	textSectionLen := uint64(len(textSectionData) - 1)
+
+	exe, err := link.OpenExecutable(path)
+	if err != nil {
+		log("failed to open executable", err)
+		return nil
+	}
+
+	var links []link.Link
+	for _, s := range symbols {
+		if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 {
+			continue
+		}
+		switch s.Name {
+		case writeSymbol, readSymbol:
+		default:
+			continue
+		}
+		address := s.Value
+		for _, p := range ef.Progs {
+			if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 {
+				continue
+			}
+
+			if p.Vaddr <= s.Value && s.Value < (p.Vaddr+p.Memsz) {
+				address = s.Value - p.Vaddr + p.Off
+				break
+			}
+		}
+		switch s.Name {
+		case writeSymbol:
+			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address})
+			if err != nil {
+				log("failed to attach write_enter uprobe", err)
+				return nil
+			}
+			links = append(links, l)
+		case readSymbol:
+			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address})
+			if err != nil {
+				log("failed to attach read_enter uprobe", err)
+				return nil
+			}
+			links = append(links, l)
+			sStart := s.Value - textSection.Addr
+			sEnd := sStart + s.Size
+			if sEnd > textSectionLen {
+				continue
+			}
+			sBytes := textSectionData[sStart:sEnd]
+			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
+			if len(returnOffsets) == 0 {
+				log("failed to attach read_exit uprobe", fmt.Errorf("no return offsets found"))
+				return nil
+			}
+			for _, offset := range returnOffsets {
+				l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_exit"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
+				if err != nil {
+					log("failed to attach read_exit uprobe", err)
+					return nil
+				}
+				links = append(links, l)
+			}
+		}
+	}
+	if len(links) == 0 {
+		return nil
+	}
+	log("crypto/tls uprobes attached", nil)
+	return links
+}
+
+func getReturnOffsets(machine elf.Machine, instructions []byte) []int {
+	var res []int
+	switch machine {
+	case elf.EM_X86_64:
+		for i := 0; i < len(instructions); {
+			ins, err := x86asm.Decode(instructions[i:], 64)
+			if err == nil && ins.Op == x86asm.RET {
+				res = append(res, i)
+			}
+			i += ins.Len
+		}
+	case elf.EM_AARCH64:
+		for i := 0; i < len(instructions); {
+			ins, err := arm64asm.Decode(instructions[i:])
+			if err == nil && ins.Op == arm64asm.RET {
+				res = append(res, i)
+			}
+			i += 4
+		}
+	}
+	return res
+}

+ 48 - 29
ebpftracer/tracer.go

@@ -134,31 +134,47 @@ type Event struct {
 }
 
 type Tracer struct {
+	kernelVersion    string
+	disableL7Tracing bool
+
 	collection *ebpf.Collection
 	readers    map[string]*perf.Reader
 	links      []link.Link
+	uprobes    map[string]*ebpf.Program
 }
 
-func NewTracer(events chan<- Event, kernelVersion string, disableL7Tracing bool) (*Tracer, error) {
-	t := &Tracer{readers: map[string]*perf.Reader{}}
-	if err := t.ebpf(events, kernelVersion, disableL7Tracing); err != nil {
-		return nil, err
-	}
+func NewTracer(kernelVersion string, disableL7Tracing bool) *Tracer {
 	if disableL7Tracing {
 		klog.Infoln("L7 tracing is disabled")
 	}
+	return &Tracer{
+		kernelVersion:    kernelVersion,
+		disableL7Tracing: disableL7Tracing,
+
+		readers: map[string]*perf.Reader{},
+		uprobes: map[string]*ebpf.Program{},
+	}
+}
+
+func (t *Tracer) Run(events chan<- Event) error {
+	if err := t.ebpf(events); err != nil {
+		return err
+	}
 	if err := t.init(events); err != nil {
-		return nil, err
+		return err
 	}
-	return t, nil
+	return nil
 }
 
 func (t *Tracer) Close() {
+	for _, p := range t.uprobes {
+		_ = p.Close()
+	}
 	for _, l := range t.links {
-		l.Close()
+		_ = l.Close()
 	}
 	for _, r := range t.readers {
-		r.Close()
+		_ = r.Close()
 	}
 	t.collection.Close()
 }
@@ -208,29 +224,32 @@ type perfMap struct {
 	event                 rawEvent
 }
 
-func (t *Tracer) ebpf(ch chan<- Event, kernelVersion string, disableL7Tracing bool) error {
-	kv := "v" + common.KernelMajorMinor(kernelVersion)
+func (t *Tracer) ebpf(ch chan<- Event) error {
+	if _, ok := ebpfProg[runtime.GOARCH]; !ok {
+		return fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
+	}
+	kv := "v" + common.KernelMajorMinor(t.kernelVersion)
 	var prg []byte
-	for _, p := range ebpfProg {
+	for _, p := range ebpfProg[runtime.GOARCH] {
 		if semver.Compare(kv, p.v) >= 0 {
 			prg = p.p
 			break
 		}
 	}
 	if len(prg) == 0 {
-		return fmt.Errorf("unsupported kernel version: %s", kernelVersion)
+		return fmt.Errorf("unsupported kernel version: %s", t.kernelVersion)
 	}
 
 	if _, err := os.Stat("/sys/kernel/debug/tracing"); err != nil {
 		return fmt.Errorf("kernel tracing is not available: %w", err)
 	}
 
-	spec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(prg))
+	collectionSpec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(prg))
 	if err != nil {
-		return fmt.Errorf("failed to load spec: %w", err)
+		return fmt.Errorf("failed to load collection spec: %w", err)
 	}
 	_ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY})
-	c, err := ebpf.NewCollection(spec)
+	c, err := ebpf.NewCollection(collectionSpec)
 	if err != nil {
 		return fmt.Errorf("failed to load collection: %w", err)
 	}
@@ -244,7 +263,7 @@ func (t *Tracer) ebpf(ch chan<- Event, kernelVersion string, disableL7Tracing bo
 		{name: "file_events", event: &fileEvent{}, perCPUBufferSizePages: 4},
 	}
 
-	if !disableL7Tracing {
+	if !t.disableL7Tracing {
 		perfMaps = append(perfMaps, perfMap{name: "l7_events", event: &l7Event{}, perCPUBufferSizePages: 16})
 	}
 
@@ -258,13 +277,10 @@ func (t *Tracer) ebpf(ch chan<- Event, kernelVersion string, disableL7Tracing bo
 		go runEventsReader(pm.name, r, ch, pm.event)
 	}
 
-	for name, spec := range spec.Programs {
-		p := t.collection.Programs[name]
-		if runtime.GOARCH == "arm64" && (spec.Name == "sys_enter_open" || spec.Name == "sys_exit_open") {
-			continue
-		}
-		if disableL7Tracing {
-			switch spec.Name {
+	for _, programSpec := range collectionSpec.Programs {
+		program := t.collection.Programs[programSpec.Name]
+		if t.disableL7Tracing {
+			switch programSpec.Name {
 			case "sys_enter_writev", "sys_enter_write", "sys_enter_sendto":
 				continue
 			case "sys_enter_read", "sys_enter_readv", "sys_enter_recvfrom":
@@ -273,14 +289,17 @@ func (t *Tracer) ebpf(ch chan<- Event, kernelVersion string, disableL7Tracing bo
 				continue
 			}
 		}
-		var err error
 		var l link.Link
-		switch spec.Type {
+		switch programSpec.Type {
 		case ebpf.TracePoint:
-			parts := strings.SplitN(spec.AttachTo, "/", 2)
-			l, err = link.Tracepoint(parts[0], parts[1], p, nil)
+			parts := strings.SplitN(programSpec.AttachTo, "/", 2)
+			l, err = link.Tracepoint(parts[0], parts[1], program, nil)
 		case ebpf.Kprobe:
-			l, err = link.Kprobe(spec.AttachTo, p, nil)
+			if strings.HasPrefix(programSpec.SectionName, "uprobe/") {
+				t.uprobes[programSpec.Name] = program
+				continue
+			}
+			l, err = link.Kprobe(programSpec.AttachTo, program, nil)
 		}
 		if err != nil {
 			t.Close()

+ 2 - 1
ebpftracer/tracer_test.go

@@ -323,7 +323,8 @@ func runTracer(t *testing.T, verbose bool) (func() *Event, func()) {
 	assert.NoError(t, unix.Uname(&uname))
 
 	go func() {
-		tt, err := NewTracer(events, string(bytes.Split(uname.Release[:], []byte{0})[0]), false)
+		tt := NewTracer(string(bytes.Split(uname.Release[:], []byte{0})[0]), false)
+		err := tt.Run(events)
 		require.NoError(t, err)
 		<-done
 		tt.Close()

+ 17 - 14
go.mod

@@ -4,10 +4,10 @@ go 1.19
 
 require (
 	cloud.google.com/go/compute/metadata v0.2.3
-	github.com/cilium/cilium v1.13.0
-	github.com/cilium/ebpf v0.9.4-0.20221102092914-a9cf21df64c2
-	github.com/containerd/cgroups v1.0.1
-	github.com/containerd/containerd v1.5.0-rc.0
+	github.com/cilium/cilium v1.13.2
+	github.com/cilium/ebpf v0.11.0
+	github.com/containerd/cgroups v1.0.3
+	github.com/containerd/containerd v1.5.17
 	github.com/coreos/go-systemd/v22 v22.5.0
 	github.com/coroot/logparser v1.0.6
 	github.com/docker/docker v20.10.21+incompatible
@@ -25,9 +25,10 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0
 	go.opentelemetry.io/otel/sdk v1.14.0
 	go.opentelemetry.io/otel/trace v1.14.0
-	golang.org/x/mod v0.7.0
+	golang.org/x/arch v0.4.0
+	golang.org/x/mod v0.11.0
 	golang.org/x/net v0.7.0
-	golang.org/x/sys v0.6.0
+	golang.org/x/sys v0.10.0
 	golang.org/x/time v0.3.0
 	gopkg.in/alecthomas/kingpin.v2 v2.2.6
 	gopkg.in/yaml.v2 v2.4.0
@@ -37,8 +38,8 @@ require (
 
 require (
 	cloud.google.com/go/compute v1.15.1 // indirect
-	github.com/Microsoft/go-winio v0.5.1 // indirect
-	github.com/Microsoft/hcsshim v0.8.16 // indirect
+	github.com/Microsoft/go-winio v0.5.2 // indirect
+	github.com/Microsoft/hcsshim v0.8.25 // indirect
 	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
 	github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
 	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
@@ -48,9 +49,9 @@ require (
 	github.com/cenkalti/backoff/v4 v4.2.0 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/cilium/workerpool v1.1.3 // indirect
-	github.com/containerd/continuity v0.1.0 // indirect
+	github.com/containerd/continuity v0.3.0 // indirect
 	github.com/containerd/fifo v1.0.0 // indirect
-	github.com/containerd/ttrpc v1.0.2 // indirect
+	github.com/containerd/ttrpc v1.1.0 // indirect
 	github.com/containerd/typeurl v1.0.2 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/docker/distribution v2.8.1+incompatible // indirect
@@ -87,16 +88,18 @@ require (
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
 	github.com/magiconair/properties v1.8.6 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mdlayher/genetlink v1.2.0 // indirect
 	github.com/mdlayher/netlink v1.6.0 // indirect
 	github.com/mdlayher/socket v0.2.3 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/moby/locker v1.0.1 // indirect
 	github.com/moby/sys/mountinfo v0.4.1 // indirect
+	github.com/moby/term v0.5.0 // indirect
 	github.com/oklog/ulid v1.3.1 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.0.2 // indirect
-	github.com/opencontainers/runc v1.0.1 // indirect
+	github.com/opencontainers/runc v1.0.2 // indirect
 	github.com/opencontainers/selinux v1.8.2 // indirect
 	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
@@ -124,7 +127,7 @@ require (
 	go.opentelemetry.io/proto/otlp v0.19.0 // indirect
 	go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
 	go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
-	golang.org/x/exp v0.0.0-20221106115401-f9659909a136 // indirect
+	golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b // indirect
 	golang.org/x/sync v0.1.0 // indirect
 	golang.org/x/text v0.7.0 // indirect
 	google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
@@ -132,7 +135,7 @@ require (
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/client-go v0.26.0 // indirect
+	k8s.io/client-go v0.26.4 // indirect
 )
 
 replace (

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 43 - 364
go.sum


+ 2 - 2
main.go

@@ -107,11 +107,11 @@ func main() {
 		klog.Exitln(err)
 	}
 
-	cs, err := containers.NewRegistry(registerer, kv)
+	cr, err := containers.NewRegistry(registerer, kv)
 	if err != nil {
 		klog.Exitln(err)
 	}
-	defer cs.Close()
+	defer cr.Close()
 
 	http.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: logger{}, Registry: registerer}))
 	klog.Infoln("listening on:", *flags.ListenAddress)

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä