quark.h (383 lines of code) (raw):

/* SPDX-License-Identifier: Apache-2.0 */ /* Copyright (c) 2024 Elastic NV */ #ifndef _QUARK_H_ #define _QUARK_H_ /* Version is shared between library and utilities */ #define QUARK_VERSION "0.4a" /* Misc types */ #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> /* Compat, tree.h, queue.h */ #include "compat.h" /* Misc */ #ifndef ALIGN_UP #define ALIGN_UP(_p, _b) (((u64)(_p) + ((_b) - 1)) & ~((_b) - 1)) #endif /* Temporary until we have proper env debugging */ extern int quark_verbose; /* quark.c */ struct raw_event; struct quark_event; struct quark_process; struct quark_process_iter; struct quark_cmdline_iter; struct quark_socket; struct quark_socket_iter; struct quark_sockaddr; struct quark_queue; struct quark_queue_attr; struct quark_queue_stats; struct raw_event *raw_event_alloc(int); void raw_event_free(struct raw_event *); int raw_event_insert(struct quark_queue *, struct raw_event *); void quark_queue_default_attr(struct quark_queue_attr *); int quark_queue_open(struct quark_queue *, struct quark_queue_attr *); void quark_queue_close(struct quark_queue *); int quark_queue_populate(struct quark_queue *); int quark_queue_block(struct quark_queue *); const struct quark_event *quark_queue_get_event(struct quark_queue *); int quark_queue_get_epollfd(struct quark_queue *); void quark_queue_get_stats(struct quark_queue *, struct quark_queue_stats *); int quark_dump_process_cache_graph(struct quark_queue *, FILE *); int quark_dump_raw_event_graph(struct quark_queue *, FILE *, FILE *); int quark_event_dump(const struct quark_event *, FILE *); void quark_process_iter_init(struct quark_process_iter *, struct quark_queue *); const struct quark_process *quark_process_iter_next(struct quark_process_iter *); const struct quark_process *quark_process_lookup(struct quark_queue *, int); void quark_cmdline_iter_init(struct quark_cmdline_iter *, const char *, size_t); const char *quark_cmdline_iter_next(struct quark_cmdline_iter *); void quark_socket_iter_init(struct quark_socket_iter *, struct quark_queue *); const struct quark_socket *quark_socket_iter_next(struct quark_socket_iter *); const struct quark_socket *quark_socket_lookup(struct quark_queue *, struct quark_sockaddr *, struct quark_sockaddr *); /* btf.c */ struct quark_btf_target { const char *dotname; ssize_t offset; /* in bytes, not bits */ }; struct quark_btf { char *kname; struct quark_btf_target targets[]; }; struct quark_btf *quark_btf_open(void); struct quark_btf *quark_btf_open2(const char *, const char *); struct quark_btf *quark_btf_open_hub(const char *); void quark_btf_close(struct quark_btf *); ssize_t quark_btf_offset(struct quark_btf *, const char *); struct btf; s32 btf_root_offset(struct btf *, const char *, int); int btf_number_of_params(struct btf *, const char *); int btf_index_of_param(struct btf *, const char *, const char *); /* bpf_queue.c */ int bpf_queue_open(struct quark_queue *); /* kprobe_queue.c */ int kprobe_queue_open(struct quark_queue *); /* XXX terrible name XXX */ struct args { char *buf; size_t buf_len; int argc; const char *argv[]; }; /* qutil.c */ struct qstr { char *p; char small[64]; }; ssize_t qread(int, void *, size_t); int qwrite(int, const void *, size_t); ssize_t qreadlinkat(int, const char *, char *, size_t); int isnumber(const char *); ssize_t readlineat(int, const char *, char *, size_t); int strtou64(u64 *, const char *, int); char *find_line(FILE *, const char *); char *find_line_p(const char *, const char *); char *load_file_nostat(int, size_t *); enum quark_verbosity_levels { QUARK_VL_SILENT, QUARK_VL_WARN, QUARK_VL_DEBUG, }; #define qlog(pri, do_errno, fmt, ...) \ qlog_func(pri, do_errno, __func__, __LINE__, fmt, ##__VA_ARGS__) #define qlogx(pri, do_errno, fmt, ...) \ qlog_func(pri, do_errno, __func__, __LINE__, fmt, ##__VA_ARGS__) #define qwarn(fmt, ...) qlog(QUARK_VL_WARN, 1, fmt, ##__VA_ARGS__) #define qwarnx(fmt, ...) qlog(QUARK_VL_WARN, 0, fmt, ##__VA_ARGS__) #define qdebug(fmt, ...) qlog(QUARK_VL_DEBUG, 1, fmt, ##__VA_ARGS__) #define qdebugx(fmt, ...) qlog(QUARK_VL_DEBUG, 0, fmt, ##__VA_ARGS__) void qlog_func(int, int, const char *, int, const char *, ...) __attribute__((format(printf, 5,6))); /* * Time helpers */ #ifndef NS_PER_S #define NS_PER_S 1000000000ULL #endif /* NS_PER_S */ #ifndef NS_PER_MS #define NS_PER_MS 1000000ULL #endif /* NS_PER_MS */ #ifndef MS_TO_NS #define MS_TO_NS(_x) ((u64)(_x) * NS_PER_MS) #endif /* MS_TO_NS */ /* * Generic exported constants */ #define QUARK_MAX_PACKET 2048 /* * Raw events */ enum raw_types { RAW_INVALID, RAW_EXEC, RAW_WAKE_UP_NEW_TASK, RAW_EXIT_THREAD, RAW_COMM, RAW_EXEC_CONNECTOR, RAW_SOCK_CONN, RAW_PACKET, RAW_NUM_TYPES /* must be last */ }; struct raw_comm { char comm[16]; }; struct raw_task { u64 cap_inheritable; u64 cap_permitted; u64 cap_effective; u64 cap_bset; u64 cap_ambient; u64 start_boottime; u32 uid; u32 gid; u32 suid; u32 sgid; u32 euid; u32 egid; u32 pgid; u32 sid; u32 ppid; s32 exit_code; /* only available at exit */ u64 exit_time_event; /* only available at exit */ u32 tty_major; u32 tty_minor; u32 uts_inonum; u32 ipc_inonum; u32 mnt_inonum; u32 net_inonum; char *cwd; char comm[16]; }; struct raw_exec { #define RAW_EXEC_F_EXT (1 << 0) int flags; char *filename; /* available if RAW_EXEC_F_EXT */ struct { char *args; size_t args_len; struct raw_task task; } ext; }; struct raw_exec_connector { char *args; size_t args_len; struct raw_task task; }; /* not like sockaddr{}, we won't use this on sockets anyway */ struct quark_sockaddr { int af; union { u32 addr4; u8 addr6[16]; }; u16 port; }; enum sock_conn { SOCK_CONN_INVALID, SOCK_CONN_CLOSE, SOCK_CONN_ACCEPT, SOCK_CONN_CONNECT, }; struct raw_sock_conn { struct quark_sockaddr local; struct quark_sockaddr remote; enum sock_conn conn; }; enum quark_packet_direction { QUARK_PACKET_DIR_INVALID, QUARK_PACKET_DIR_EGRESS, QUARK_PACKET_DIR_INGRESS, }; enum quark_packet_origin { QUARK_PACKET_ORIGIN_INVALID, QUARK_PACKET_ORIGIN_DNS, }; struct quark_packet { enum quark_packet_direction direction; enum quark_packet_origin origin; size_t orig_len; size_t cap_len; char data[]; }; struct raw_packet { struct quark_packet *quark_packet; }; struct raw_event { RB_ENTRY(raw_event) entry_by_time; RB_ENTRY(raw_event) entry_by_pidtime; TAILQ_HEAD(agg_queue, raw_event) agg_queue; TAILQ_ENTRY(raw_event) agg_entry; u32 opid; u32 pid; u32 tid; u32 cpu; u64 time; int type; union { struct raw_exec exec; struct raw_comm comm; struct raw_task task; struct raw_exec_connector exec_connector; struct raw_sock_conn sock_conn; struct raw_packet packet; }; }; /* * Raw Event Tree by time, where RB_MIN() is the oldest element in the tree, no * clustering of pids so we can easily get the oldest event. */ RB_HEAD(raw_event_by_time, raw_event); /* * Raw Event Tree by pid and time, this creates clusters of the same pid which * are then organized by time, this is used in assembly and aggregation, if we * used the 'by_time' tree, we would have to traverse the full tree in case of a * miss. */ RB_HEAD(raw_event_by_pidtime, raw_event); struct quark_event { #define QUARK_EV_FORK (1 << 0) #define QUARK_EV_EXEC (1 << 1) #define QUARK_EV_EXIT (1 << 2) #define QUARK_EV_SETPROCTITLE (1 << 3) #define QUARK_EV_SOCK_CONN_ESTABLISHED (1 << 4) #define QUARK_EV_SOCK_CONN_CLOSED (1 << 5) #define QUARK_EV_PACKET (1 << 6) #define QUARK_EV_BYPASS (1 << 7) u64 events; const struct quark_process *process; const struct quark_socket *socket; struct quark_packet *packet; const void *bypass; }; /* * Process cache, used to enrich single events */ RB_HEAD(process_by_pid, quark_process); /* * Socket tree, indexed by src and dst */ RB_HEAD(socket_by_src_dst, quark_socket); enum { QUARK_TTY_UNKNOWN, QUARK_TTY_PTS, QUARK_TTY_TTY, QUARK_TTY_CONSOLE, }; /* * The values for proc_entry_leader_type */ enum { QUARK_ELT_UNKNOWN, QUARK_ELT_INIT, QUARK_ELT_KTHREAD, QUARK_ELT_SSHD, QUARK_ELT_SSM, QUARK_ELT_CONTAINER, QUARK_ELT_TERM, QUARK_ELT_CONSOLE, }; enum gc_type { GC_INVALID, GC_PROCESS, GC_SOCKET, }; struct gc_link { TAILQ_ENTRY(gc_link) gc_entry; u64 gc_time; enum gc_type gc_type; }; /* * gc queue, after processes or sockets are are marked for deletion, they still * get a grace time of qq->cache_grace_time before removal, this is to allow * lookups from users on processes and sockets that have just vanished. */ TAILQ_HEAD(gc_queue, gc_link); /* * Main external working set, user passes this back and forth, members only have * a meaning if its respective flag is set, say proc_cap_inheritable should only * be meaningful if flags & QUARK_F_PROC. */ struct quark_process { struct gc_link gc; /* must be first */ RB_ENTRY(quark_process) entry_by_pid; /* Always present */ u32 pid; #define QUARK_F_PROC (1 << 0) #define QUARK_F_EXIT (1 << 1) #define QUARK_F_COMM (1 << 2) #define QUARK_F_FILENAME (1 << 3) #define QUARK_F_CMDLINE (1 << 4) #define QUARK_F_CWD (1 << 5) u64 flags; /* QUARK_F_PROC */ u64 proc_cap_inheritable; u64 proc_cap_permitted; u64 proc_cap_effective; u64 proc_cap_bset; u64 proc_cap_ambient; u64 proc_time_boot; u32 proc_ppid; u32 proc_uid; u32 proc_gid; u32 proc_suid; u32 proc_sgid; u32 proc_euid; u32 proc_egid; u32 proc_pgid; u32 proc_sid; u32 proc_tty_major; u32 proc_tty_minor; u32 proc_entry_leader_type; u32 proc_entry_leader; u32 proc_uts_inonum; u32 proc_ipc_inonum; u32 proc_mnt_inonum; u32 proc_net_inonum; /* QUARK_F_EXIT */ s32 exit_code; u64 exit_time_event; /* QUARK_F_COMM */ char comm[16]; /* QUARK_F_FILENAME */ char *filename; /* QUARK_F_CMDLINE */ size_t cmdline_len; char *cmdline; /* QUARK_F_CWD */ char *cwd; }; struct quark_process_iter { struct quark_queue *qq; struct quark_process *qp; }; struct quark_cmdline_iter { const char *cmdline; size_t cmdline_len; size_t off; }; struct quark_socket { struct gc_link gc; /* must be first */ RB_ENTRY(quark_socket) entry_by_src_dst; struct quark_sockaddr local; struct quark_sockaddr remote; u32 pid_origin; u32 pid_last_use; u64 established_time; u64 close_time; int from_scrape; }; struct quark_socket_iter { struct quark_queue *qq; struct quark_socket *qsk; }; struct quark_queue_stats { u64 insertions; u64 removals; u64 aggregations; u64 non_aggregations; u64 lost; u64 garbage_collections; int backend; /* active backend, QQ_EBPF or QQ_KPROBE */ /* TODO u64 peak_nodes; */ }; struct quark_queue_ops { int (*open)(struct quark_queue *); int (*populate)(struct quark_queue *); int (*update_stats)(struct quark_queue *); void (*close)(struct quark_queue *); }; struct quark_queue_attr { #define QQ_THREAD_EVENTS (1 << 0) #define QQ_KPROBE (1 << 1) #define QQ_EBPF (1 << 2) #define QQ_MIN_AGG (1 << 3) #define QQ_ENTRY_LEADER (1 << 4) #define QQ_SOCK_CONN (1 << 5) #define QQ_DNS (1 << 6) #define QQ_BYPASS (1 << 7) #define QQ_FILE (1 << 8) #define QQ_ALL_BACKENDS (QQ_KPROBE | QQ_EBPF) int flags; int max_length; int cache_grace_time; /* in ms */ int hold_time; /* in ms */ }; /* * Quark Queue (qq) is the main structure the user interacts with, it acts as * our main storage datastructure. */ struct quark_queue { struct raw_event_by_time raw_event_by_time; struct raw_event_by_pidtime raw_event_by_pidtime; struct process_by_pid process_by_pid; struct gc_queue event_gc; struct socket_by_src_dst socket_by_src_dst; struct quark_event event_storage; struct quark_queue_stats stats; const u8 (*agg_matrix)[RAW_NUM_TYPES]; int flags; int length; int max_length; u64 cache_grace_time; /* in ns */ int hold_time; /* in ms */ int epollfd; /* Backend related state */ struct quark_queue_ops *queue_ops; void *queue_be; }; #endif /* _QUARK_H_ */