From ac431bd34743f0a30e6f82732aede9ea6720e20b Mon Sep 17 00:00:00 2001 From: Hengqi Chen Date: Mon, 31 May 2021 20:31:59 +0800 Subject: [PATCH] libbpf-tools: add solisten Signed-off-by: Hengqi Chen --- libbpf-tools/.gitignore | 1 + libbpf-tools/Makefile | 1 + libbpf-tools/solisten.bpf.c | 101 ++++++++++++++++++ libbpf-tools/solisten.c | 202 ++++++++++++++++++++++++++++++++++++ libbpf-tools/solisten.h | 17 +++ 5 files changed, 322 insertions(+) create mode 100644 libbpf-tools/solisten.bpf.c create mode 100644 libbpf-tools/solisten.c create mode 100644 libbpf-tools/solisten.h diff --git a/libbpf-tools/.gitignore b/libbpf-tools/.gitignore index 63f9a913..422d8604 100644 --- a/libbpf-tools/.gitignore +++ b/libbpf-tools/.gitignore @@ -31,6 +31,7 @@ /runqlen /runqslower /softirqs +/solisten /statsnoop /syscount /tcpconnect diff --git a/libbpf-tools/Makefile b/libbpf-tools/Makefile index 75b67c32..f0f67626 100644 --- a/libbpf-tools/Makefile +++ b/libbpf-tools/Makefile @@ -42,6 +42,7 @@ APPS = \ runqlen \ runqslower \ softirqs \ + solisten \ statsnoop \ syscount \ tcpconnect \ diff --git a/libbpf-tools/solisten.bpf.c b/libbpf-tools/solisten.bpf.c new file mode 100644 index 00000000..c049ad4b --- /dev/null +++ b/libbpf-tools/solisten.bpf.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2021 Hengqi Chen */ +#include +#include +#include +#include +#include +#include "solisten.h" + +#define MAX_ENTRIES 10240 +#define AF_INET 2 +#define AF_INET6 10 + +const volatile pid_t target_pid = 0; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, MAX_ENTRIES); + __type(key, __u32); + __type(value, struct event); +} values SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} events SEC(".maps"); + +static void fill_event(struct event *event, struct socket *sock) +{ + __u16 family, type; + struct sock *sk; + struct inet_sock *inet; + + sk = BPF_CORE_READ(sock, sk); + inet = (struct inet_sock *)sk; + family = BPF_CORE_READ(sk, __sk_common.skc_family); + type = BPF_CORE_READ(sock, type); + + event->proto = ((__u32)family << 16) | type; + event->port = bpf_ntohs(BPF_CORE_READ(inet, inet_sport)); + if (family == AF_INET) + event->addr[0] = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + else if (family == AF_INET6) + BPF_CORE_READ_INTO(event->addr, sk, __sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + bpf_get_current_comm(event->task, sizeof(event->task)); +} + +SEC("kprobe/inet_listen") +int BPF_KPROBE(inet_listen_entry, struct socket *sock, int backlog) +{ + __u64 pid_tgid = bpf_get_current_pid_tgid(); + __u32 pid = pid_tgid >> 32; + __u32 tid = (__u32)pid_tgid; + struct event event = {}; + + if (target_pid && target_pid != pid) + return 0; + + fill_event(&event, sock); + event.pid = pid; + event.backlog = backlog; + bpf_map_update_elem(&values, &tid, &event, BPF_ANY); + return 0; +} + +SEC("kretprobe/inet_listen") +int BPF_KRETPROBE(inet_listen_exit, int ret) +{ + __u32 tid = bpf_get_current_pid_tgid(); + struct event *eventp; + + eventp = bpf_map_lookup_elem(&values, &tid); + if (!eventp) + return 0; + + eventp->ret = ret; + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, eventp, sizeof(*eventp)); + bpf_map_delete_elem(&values, &tid); + return 0; +} + +SEC("fexit/inet_listen") +int BPF_PROG(inet_listen_fexit, struct socket *sock, int backlog, int ret) +{ + __u64 pid_tgid = bpf_get_current_pid_tgid(); + __u32 pid = pid_tgid >> 32; + struct event event = {}; + + if (target_pid && target_pid != pid) + return 0; + + fill_event(&event, sock); + event.pid = pid; + event.backlog = backlog; + event.ret = ret; + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); + return 0; +} + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; diff --git a/libbpf-tools/solisten.c b/libbpf-tools/solisten.c new file mode 100644 index 00000000..b5f68a65 --- /dev/null +++ b/libbpf-tools/solisten.c @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ + +/* + * solisten Trace IPv4 and IPv6 listen syscalls + * + * Copyright (c) 2021 Hengqi Chen + * + * Based on solisten(8) from BCC by Jean-Tiare Le Bigot + * 31-May-2021 Hengqi Chen Created this. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include "solisten.h" +#include "solisten.skel.h" +#include "trace_helpers.h" + +#define PERF_BUFFER_PAGES 16 +#define PERF_POLL_TIMEOUT_MS 100 +#define warn(...) fprintf(stderr, __VA_ARGS__) + +static volatile sig_atomic_t exiting = 0; + +static pid_t target_pid = 0; +static bool emit_timestamp = false; + +const char *argp_program_version = "solisten 0.1"; +const char *argp_program_bug_address = + "https://github.com/iovisor/bcc/tree/master/libbpf-tools"; +const char argp_program_doc[] = +"Trace IPv4 and IPv6 listen syscalls.\n" +"\n" +"USAGE: solisten [-h] [-t] [-p PID]\n" +"\n" +"EXAMPLES:\n" +" solisten # trace listen syscalls\n" +" solisten -t # output with timestamp\n" +" solisten -p 1216 # only trace PID 1216\n"; + +static const struct argp_option opts[] = { + {"pid", 'p', "PID", 0, "Process ID to trace"}, + {"timestamp", 't', NULL, 0, "Include timestamp on output"}, + {NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"}, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + long pid; + + switch (key) { + case 'p': + errno = 0; + pid = strtol(arg, NULL, 10); + if (errno || pid <= 0) { + warn("Invalid PID: %s\n", arg); + argp_usage(state); + } + target_pid = pid; + break; + case 't': + emit_timestamp = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static void sig_int(int signo) +{ + exiting = 1; +} + +static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) +{ + const struct event *e = data; + time_t t; + struct tm *tm; + char ts[32], proto[16], addr[48] = {}; + __u16 family = e->proto >> 16; + __u16 type = (__u16)e->proto; + const char *prot; + + if (emit_timestamp) { + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + printf("%8s ", ts); + } + + if (type == SOCK_STREAM) + prot = "TCP"; + else if (type == SOCK_DGRAM) + prot = "UDP"; + else + prot = "UNK"; + if (family == AF_INET) + snprintf(proto, sizeof(proto), "%sv4", prot); + else /* family == AF_INET6 */ + snprintf(proto, sizeof(proto), "%sv6", prot); + inet_ntop(family, e->addr, addr, sizeof(addr)); + printf("%-7d %-16s %-3d %-7d %-5s %-5d %-32s\n", + e->pid, e->task, e->ret, e->backlog, proto, e->port, addr); +} + +static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) +{ + warn("lost %llu events on CPU #%d\n", lost_cnt, cpu); +} + +int main(int argc, char **argv) +{ + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + struct perf_buffer_opts pb_opts; + struct perf_buffer *pb = NULL; + struct solisten_bpf *obj; + int err; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + err = bump_memlock_rlimit(); + if (err) { + warn("failed to increase rlimit: %d\n", err); + return 1; + } + + obj = solisten_bpf__open(); + if (!obj) { + warn("failed to open BPF object\n"); + return 1; + } + + obj->rodata->target_pid = target_pid; + + if (fentry_exists("inet_listen", NULL)) { + bpf_program__set_autoload(obj->progs.inet_listen_entry, false); + bpf_program__set_autoload(obj->progs.inet_listen_exit, false); + } else { + bpf_program__set_autoload(obj->progs.inet_listen_fexit, false); + } + + err = solisten_bpf__load(obj); + if (err) { + warn("failed to load BPF object: %d\n", err); + goto cleanup; + } + + err = solisten_bpf__attach(obj); + if (err) { + warn("failed to attach BPF programs: %d\n", err); + goto cleanup; + } + + pb_opts.sample_cb = handle_event; + pb_opts.lost_cb = handle_lost_events; + pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &pb_opts); + err = libbpf_get_error(pb); + if (err) { + warn("failed to open perf buffer: %d\n", err); + goto cleanup; + } + + if (signal(SIGINT, sig_int) == SIG_ERR) { + warn("can't set signal handler: %s\n", strerror(-errno)); + goto cleanup; + } + + if (emit_timestamp) + printf("%-8s ", "TIME(s)"); + printf("%-7s %-16s %-3s %-7s %-5s %-5s %-32s\n", + "PID", "COMM", "RET", "BACKLOG", "PROTO", "PORT", "ADDR"); + + while (1) { + if ((err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS)) < 0) + break; + if (exiting) + goto cleanup; + } + warn("error polling perf buffer: %d\n", err); + +cleanup: + perf_buffer__free(pb); + solisten_bpf__destroy(obj); + + return err != 0; +} diff --git a/libbpf-tools/solisten.h b/libbpf-tools/solisten.h new file mode 100644 index 00000000..0b57b3ec --- /dev/null +++ b/libbpf-tools/solisten.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __SOLISTEN_H +#define __SOLISTEN_H + +#define TASK_COMM_LEN 16 + +struct event { + __u32 addr[4]; + __u32 pid; + __u32 proto; + int backlog; + int ret; + short port; + char task[TASK_COMM_LEN]; +}; + +#endif /* __SOLISTEN_H */ -- 2.34.1