Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions containers/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ func (c *Container) Collect(ch chan<- prometheus.Metric) {
if process.isGolangApp {
appTypes["golang"] = struct{}{}
}
if process.isRustApp {
appTypes["rust"] = struct{}{}
}
switch {
case proc.IsJvm(cmdline):
jvm, jMetrics := jvmMetrics(pid)
Expand Down Expand Up @@ -1251,6 +1254,14 @@ func (c *Container) attachTlsUprobes(tracer *ebpftracer.Tracer, pid uint32) {
}
p.goTlsUprobesChecked = true
}
if !p.rustlsUprobesChecked {
key, isRustApp := tracer.AttachRustlsUprobes(pid)
p.isRustApp = isRustApp
if key != nil {
p.uprobeKeys = append(p.uprobeKeys, *key)
}
p.rustlsUprobesChecked = true
}
}

func resolveFd(pid uint32, fd uint64) (mntId string, logPath string) {
Expand Down
2 changes: 2 additions & 0 deletions containers/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ type Process struct {
tracer *ebpftracer.Tracer
dotNetMonitor *DotNetMonitor
isGolangApp bool
isRustApp bool

uprobeKeys []ebpftracer.UprobeKey
goTlsUprobesChecked bool
openSslUprobesChecked bool
rustlsUprobesChecked bool
pythonGilChecked bool
nodejsChecked bool
nodejsPrevStats *ebpftracer.NodejsStats
Expand Down
20 changes: 10 additions & 10 deletions ebpftracer/ebpf.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ebpftracer/ebpf/ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ struct trace_event_raw_sys_exit__stub {
#include "l7/l7.c"
#include "l7/gotls.c"
#include "l7/openssl.c"
#include "l7/rustls.c"

char _license[] SEC("license") = "GPL";
42 changes: 42 additions & 0 deletions ebpftracer/ebpf/l7/l7.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ struct {
__uint(max_entries, 10240);
} ssl_pending SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(key_size, sizeof(__u64));
__uint(value_size, sizeof(__u64));
__uint(max_entries, 10240);
} rustls_last_read_fd SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(key_size, sizeof(__u64));
__uint(value_size, sizeof(struct ssl_args));
__uint(max_entries, 10240);
} rustls_write_pending SEC(".maps");

#if defined(__TARGET_ARCH_x86)
#define RUSTLS_RET_IS_OK(x) (PT_REGS_RC(x) == 0)
#define RUSTLS_RET_SIZE(x) ((x)->dx)
#elif defined(__TARGET_ARCH_arm64)
#define RUSTLS_RET_IS_OK(x) (PT_REGS_RC(x) == 0)
#define RUSTLS_RET_SIZE(x) (((PT_REGS_ARM64 *)(x))->regs[1])
#endif

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, int);
Expand Down Expand Up @@ -194,6 +216,16 @@ __u64 read_iovec(char *iovec, __u64 iovlen, __u64 ret, char *buf, __u64 *total_s
static inline __attribute__((__always_inline__))
int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size, __u64 iovlen) {
__u64 id = bpf_get_current_pid_tgid();
char *plain_buf = 0;
__u64 plain_size = 0;
if (!is_tls) {
struct ssl_args *rw = bpf_map_lookup_elem(&rustls_write_pending, &id);
if (rw) {
plain_buf = rw->buf;
plain_size = rw->size;
bpf_map_delete_elem(&rustls_write_pending, &id);
}
}
__u32 zero = 0;
struct connection_id cid = {};
cid.pid = id >> 32;
Expand Down Expand Up @@ -222,6 +254,12 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,
__sync_fetch_and_add(&conn->bytes_sent, total_size);
}

if (plain_buf) {
payload = plain_buf;
size = plain_size;
is_tls = 1;
}

struct l7_request *req = bpf_map_lookup_elem(&l7_request_heap, &zero);
if (!req) {
return 0;
Expand Down Expand Up @@ -336,6 +374,10 @@ int trace_enter_write(void *ctx, __u64 fd, __u16 is_tls, char *buf, __u64 size,

static inline __attribute__((__always_inline__))
int trace_enter_read(__u64 id, __u32 pid, __u64 fd, char *buf, __u64 *ret, __u64 iovlen) {
if (bpf_map_lookup_elem(&rustls_pids, &pid)) {
bpf_map_update_elem(&rustls_last_read_fd, &id, &fd, BPF_ANY);
}

struct connection_id cid = {};
cid.pid = pid;
cid.fd = fd;
Expand Down
46 changes: 46 additions & 0 deletions ebpftracer/ebpf/l7/rustls.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
SEC("uprobe/rustls_write_enter")
int rustls_write_enter(struct pt_regs *ctx) {
__u64 tid = bpf_get_current_pid_tgid();
__u32 pid = tid >> 32;

__u8 dummy = 1;
bpf_map_update_elem(&rustls_pids, &pid, &dummy, BPF_ANY);

struct ssl_args args = {};
args.buf = (char *)PT_REGS_PARM2(ctx);
args.size = PT_REGS_PARM3(ctx);
bpf_map_update_elem(&rustls_write_pending, &tid, &args, BPF_ANY);
return 0;
}

SEC("uprobe/rustls_read_enter")
int rustls_read_enter(struct pt_regs *ctx) {
__u64 tid = bpf_get_current_pid_tgid();
__u32 pid = tid >> 32;

__u64 *fd_ptr = bpf_map_lookup_elem(&rustls_last_read_fd, &tid);
if (!fd_ptr) {
return 0;
}
__u64 fd = *fd_ptr;
bpf_map_delete_elem(&rustls_last_read_fd, &tid);

char *buf = (char *)PT_REGS_PARM2(ctx);
__u64 id = tid | IS_TLS_READ_ID;
return trace_enter_read(id, pid, fd, buf, 0, 0);
}

SEC("uprobe/rustls_read_exit")
int rustls_read_exit(struct pt_regs *ctx) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u64 id = pid_tgid | IS_TLS_READ_ID;

if (!RUSTLS_RET_IS_OK(ctx)) {
bpf_map_delete_elem(&active_reads, &id);
return 0;
}

int ret = (int)RUSTLS_RET_SIZE(ctx);
__u32 pid = pid_tgid >> 32;
return trace_exit_read(ctx, id, pid, 1, ret);
}
8 changes: 8 additions & 0 deletions ebpftracer/ebpf/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ struct {
__uint(max_entries, 10240);
} oom_info SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u8));
__uint(max_entries, 1024);
} rustls_pids SEC(".maps");

struct trace_event_raw_task_newtask__stub {
__u64 unused;
#if defined(__CTX_EXTRA_PADDING)
Expand Down Expand Up @@ -66,6 +73,7 @@ int sched_process_exit(struct trace_event_raw_sched_process_template__stub *args
bpf_map_delete_elem(&nodejs_stats, &pid);
bpf_map_delete_elem(&nodejs_prev_event_loop_iter, &pid);
bpf_map_delete_elem(&nodejs_current_io_cb, &pid);
bpf_map_delete_elem(&rustls_pids, &pid);

struct proc_event e = {
.type = EVENT_TYPE_PROCESS_EXIT,
Expand Down
41 changes: 40 additions & 1 deletion ebpftracer/elf.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ebpftracer

import (
"bytes"
"debug/elf"
"fmt"
"io"
Expand Down Expand Up @@ -72,7 +73,7 @@ func (s *Symbol) AttachUretprobes(exe *link.Executable, prog *ebpf.Program, pid
}
var links []link.Link
for _, offset := range returnOffsets {
l, err := exe.Uprobe("pthread_cond_timedwait", prog, &link.UprobeOptions{Address: s.Address(), Offset: uint64(offset), PID: int(pid)})
l, err := exe.Uprobe(s.Name(), prog, &link.UprobeOptions{Address: s.Address(), Offset: uint64(offset), PID: int(pid)})
if err != nil {
return links, err
}
Expand Down Expand Up @@ -130,6 +131,32 @@ func (f *ELFFile) GetSymbol(name string) (*Symbol, error) {
return &Symbol{s: es, f: f}, nil
}

func (f *ELFFile) FindSymbolBySubstrings(substrs ...string) *Symbol {
if f.symbols == nil {
if err := f.readSymbols(); err != nil {
return nil
}
}
for _, s := range f.symbols {
if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 || s.Value == 0 {
continue
}
name := []byte(s.Name)
match := true
for _, sub := range substrs {
if !bytes.Contains(name, []byte(sub)) {
match = false
break
}
}
if match {
es := s
return &Symbol{s: &es, f: f}
}
}
return nil
}

func (f *ELFFile) getTextSectionAndReader() (*elf.Section, io.ReadSeeker, error) {
if f.textSection == nil {
f.textSection = f.elf.Section(".text")
Expand All @@ -141,6 +168,18 @@ func (f *ELFFile) getTextSectionAndReader() (*elf.Section, io.ReadSeeker, error)
return f.textSection, f.textSectionReader, nil
}

func (f *ELFFile) IsRustBinary() bool {
section := f.elf.Section(".comment")
if section == nil {
return false
}
data, err := section.Data()
if err != nil {
return false
}
return bytes.Contains(data, []byte("rustc version"))
}

func (f *ELFFile) Close() error {
return f.elf.Close()
}
Expand Down
94 changes: 94 additions & 0 deletions ebpftracer/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,100 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32) (*UprobeKey, bool) {
return nil, isGolangApp
}

func (t *Tracer) AttachRustlsUprobes(pid uint32) (*UprobeKey, bool) {
isRustApp := false
if t.disableL7Tracing {
return nil, isRustApp
}

exePath := proc.Path(pid, "exe")

log := func(msg string, err error) {
if err != nil {
for _, s := range []string{"not found", "no such file or directory", "no such process", "permission denied"} {
if strings.HasSuffix(err.Error(), s) {
return
}
}
klog.ErrorfDepth(1, "pid=%d: %s: %s", pid, msg, err)
return
}
klog.InfofDepth(1, "pid=%d: %s", pid, msg)
}

ef, err := OpenELFFile(exePath)
if err != nil {
return nil, isRustApp
}
defer ef.Close()

if !ef.IsRustBinary() {
return nil, isRustApp
}
isRustApp = true

name, err := os.Readlink(exePath)
if err != nil {
log("failed to read name", err)
return nil, isRustApp
}

writerWrite := ef.FindSymbolBySubstrings("rustls", "Writer", "write")
if writerWrite == nil {
return nil, isRustApp
}

readerRead := ef.FindSymbolBySubstrings("rustls", "Reader", "read")
if readerRead == nil {
return nil, isRustApp
}

key, ok := t.AcquireGlobalUprobe(proc.Path(pid, "root", name), func() []link.Link {
exe, err := link.OpenExecutable(exePath)
if err != nil {
log("failed to open executable", err)
return nil
}
var links []link.Link
closeLinks := func() {
for _, l := range links {
l.Close()
}
}
l, err := writerWrite.AttachUprobe(exe, t.uprobes["rustls_write_enter"], 0)
if err != nil {
log("failed to attach rustls write_enter uprobe", err)
return nil
}
links = append(links, l)

l, err = readerRead.AttachUprobe(exe, t.uprobes["rustls_read_enter"], 0)
if err != nil {
log("failed to attach rustls read_enter uprobe", err)
closeLinks()
return nil
}
links = append(links, l)

ls, err := readerRead.AttachUretprobes(exe, t.uprobes["rustls_read_exit"], 0)
links = append(links, ls...)
if err != nil {
log("failed to attach rustls read_exit uprobe", err)
closeLinks()
return nil
}

if len(links) > 0 {
log("rustls uprobes attached", nil)
}
return links
})
if ok {
return &key, isRustApp
}
return nil, isRustApp
}

func getSslLibPath(pid uint32) string {
f, err := os.Open(proc.Path(pid, "maps"))
if err != nil {
Expand Down
Loading