diff --git a/agent/plugin/Makefile b/agent/plugin/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6d79d5c9a23bb543f0b7a63ec3648b310d25be5e --- /dev/null +++ b/agent/plugin/Makefile @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +OUTPUT := .output +CLANG ?= clang +LIBBPF_SRC := $(abspath ./libbpf-bootstrap/libbpf/src) +BPFTOOL_SRC := $(abspath ./libbpf-bootstrap/bpftool/src) +LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool + +ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ + | sed 's/arm.*/arm/' \ + | sed 's/aarch64/arm64/' \ + | sed 's/ppc64le/powerpc/' \ + | sed 's/mips.*/mips/' \ + | sed 's/riscv64/riscv/' \ + | sed 's/loongarch64/loongarch/') +VMLINUX := ./vmlinux.h + + +# Use our own libbpf API headers and Linux UAPI headers distributed with +# libbpf to avoid dependency on system-wide headers, which could be missing or +# outdated +INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) +CFLAGS := -g -Wall -Wno-unknown-pragmas +ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) + +APPS = probe + +# Get Clang's default includes on this system. We'll explicitly add these dirs +# to the includes list when compiling with `-target bpf` because otherwise some +# architecture-specific dirs will be "missing" on some architectures/distros - +# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, +# sys/cdefs.h etc. might be missing. +# +# Use '-idirafter': Don't interfere with include mechanics except where the +# build would have failed anyways. +CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ + | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') + +ifeq ($(V),1) + Q = + msg = +else + Q = @ + msg = @printf ' %-8s %s%s\n' \ + "$(1)" \ + "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ + "$(if $(3), $(3))"; + MAKEFLAGS += --no-print-directory +endif + +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +$(call allow-override,CC,$(CROSS_COMPILE)cc) +$(call allow-override,LD,$(CROSS_COMPILE)ld) + +.PHONY: all +all: $(APPS) + +.PHONY: clean +clean: + $(call msg,CLEAN) + $(Q)rm -rf $(OUTPUT) $(APPS) + +$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): + $(call msg,MKDIR,$@) + $(Q)mkdir -p $@ + +# Build libbpf +$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf + $(call msg,LIB,$@) + $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ + OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ + INCLUDEDIR= LIBDIR= UAPIDIR= \ + install + +# Build bpftool +$(BPFTOOL): | $(BPFTOOL_OUTPUT) + $(call msg,BPFTOOL,$@) + $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap + +# Build BPF code +$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) + $(call msg,BPF,$@) + $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ + $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ + -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) + +# Generate BPF skeletons +$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) + $(call msg,GEN-SKEL,$@) + $(Q)$(BPFTOOL) gen skeleton $< > $@ + +# Build user-space code +$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h + +$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ + +# Build application binary +$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + +# delete failed targets +.DELETE_ON_ERROR: + +# keep intermediate (.skel.h, .bpf.o, etc) targets +.SECONDARY: diff --git a/agent/plugin/common.bpf.h b/agent/plugin/common.bpf.h new file mode 100644 index 0000000000000000000000000000000000000000..16c67544d67aaf017f24fd5b3a0c95ccc788cdb9 --- /dev/null +++ b/agent/plugin/common.bpf.h @@ -0,0 +1,80 @@ +#ifndef __COMMON_BPF_H +#define __COMMON_BPF_H + +#include "vmlinux.h" +#include "probe.h" +#include +#include +#include +#include + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +/*rb helper*/ +struct +{ + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +/*map helper*/ +struct +{ + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_COMM *MAX_PACKET); + __type(key, struct event); + __type(value, struct ktime_info); +} udp_map SEC(".maps"); + +/*funcation hepler*/ +static __always_inline int get_current_tgid() +{ + return (int)(bpf_get_current_pid_tgid() >> PID); +} + +static __always_inline struct tcphdr *extract_tcphdr(const struct sk_buff *skb) +{ + return (struct tcphdr *)(( + _R(skb, head) + + _R(skb, transport_header))); +} + +static __always_inline struct udphdr *extract_udphdr(const struct sk_buff *skb) +{ + return (struct udphdr *)(_R(skb, head) + _R(skb, transport_header)); +} + +static __always_inline struct iphdr *extract_iphdr(const struct sk_buff *skb) +{ + return (struct iphdr *)(_R(skb, head) + _R(skb, network_header)); +} + +static __always_inline void get_udp_pkt_tuple(struct event *pkt_tuple, + struct iphdr *ip, + struct udphdr *udp) +{ + pkt_tuple->client_ip = _R(ip, saddr); + pkt_tuple->server_ip = _R(ip, daddr); + pkt_tuple->client_port = __bpf_ntohs(_R(udp, source)); + pkt_tuple->server_port = __bpf_ntohs(_R(udp, dest)); + pkt_tuple->seq = 0; + pkt_tuple->ack = 0; + pkt_tuple->tran_flag = UDP; +} + +static __always_inline void *bmloti(void *map, const void *key, const void *init) +{ + void *val; + long err; + val = bpf_map_lookup_elem(map, key); + if (val) + return val; + err = bpf_map_update_elem(map, key, init, + BPF_NOEXIST); + if (!err) + return 0; + + return bpf_map_lookup_elem(map, key); +} + +#endif // __COMMON_BPF_H \ No newline at end of file diff --git a/agent/plugin/libbpf-bootstrap b/agent/plugin/libbpf-bootstrap new file mode 160000 index 0000000000000000000000000000000000000000..2dffae1fca82fd9ef4ba15df67606cbfd2eb5412 --- /dev/null +++ b/agent/plugin/libbpf-bootstrap @@ -0,0 +1 @@ +Subproject commit 2dffae1fca82fd9ef4ba15df67606cbfd2eb5412 diff --git a/agent/plugin/probe.bpf.c b/agent/plugin/probe.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..aa945051238fd122df79cd019d23919b0a0453ee --- /dev/null +++ b/agent/plugin/probe.bpf.c @@ -0,0 +1,27 @@ +#include "common.bpf.h" +#include "traffic.bpf.h" + +// udp recieve +SEC("kprobe/udp_rcv") +int BPF_KPROBE(udp_rcv, struct sk_buff *skb) +{ + return __udp_rcv(skb); +} + +SEC("kprobe/__udp_enqueue_schedule_skb") +int BPF_KPROBE(__udp_enqueue_schedule_skb, struct sock *sk, + struct sk_buff *skb) +{ + return udp_enqueue_schedule_skb(sk, skb); +} +//send +SEC("kprobe/udp_send_skb") +int BPF_KPROBE(udp_send_skb, struct sk_buff *skb) +{ + return __udp_send_skb(skb); +} +SEC("kprobe/ip_send_skb") +int BPF_KPROBE(ip_send_skb, struct net *net, struct sk_buff *skb) +{ + return __ip_send_skb(skb); +} diff --git a/agent/plugin/probe.c b/agent/plugin/probe.c new file mode 100755 index 0000000000000000000000000000000000000000..75b860ef3a74565d72f214183d66607027c1c2dc --- /dev/null +++ b/agent/plugin/probe.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "probe.h" +#include "probe.skel.h" +#include "probe.h" + +static volatile bool exiting = false; +int udp_info = 0; + +const char argp_program_doc[] = "Trace time delay in network subsystem \n"; + +static const struct argp_option opts[] = { + {"udp", 'u', 0, 0, "trace the udp message"}, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'u': + udp_info = 1; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, +}; + +static void sig_handler(int sig) +{ + exiting = true; +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + return vfprintf(stderr, format, args); +} + +static int handle_event(void *ctx, void *packet_info, size_t data_sz) +{ + if (!udp_info) + { + return 0; + } + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct event *pack_info = packet_info; + unsigned int saddr = pack_info->client_ip; + unsigned int daddr = pack_info->server_ip; + + if (pack_info->client_port == 0 || pack_info->server_port == 0 || pack_info->pid == 0) + { + return 0; + } + printf("%-20d %-20s %-20s %-20u %-20d %-20s %-20d %-20d %-20d\n", + pack_info->pid, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->client_port, + pack_info->server_port, pack_info->comm, pack_info->tran_time, pack_info->udp_direction, pack_info->len); + return 0; +} + +int main(int argc, char **argv) +{ + struct probe_bpf *skel; + int err = 0; + struct ring_buffer *rb = NULL; + /* Parse command line arguments */ + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + /* Load and verify BPF application */ + skel = probe_bpf__open(); + if (!skel) + { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = probe_bpf__load(skel); + if (err) + { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* Attach tracepoints */ + err = probe_bpf__attach(skel); + if (err) + { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + /* Set up ring buffer polling */ + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); + if (!rb) + { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto cleanup; + } + + /* Process events */ + if (udp_info) + { + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "Pid", "Client_ip", "Server_ip", "Client_port", "Server_port", "Comm", "Tran_time/μs", "Direction", "len/byte"); + } + + while (!exiting) + { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) + { + err = 0; + break; + } + if (err < 0) + { + printf("Error polling perf buffer: %d\n", err); + break; + } + } + +cleanup: + /* Clean up */ + ring_buffer__free(rb); + probe_bpf__destroy(skel); + + return err < 0 ? -err : 0; +} diff --git a/agent/plugin/probe.h b/agent/plugin/probe.h new file mode 100644 index 0000000000000000000000000000000000000000..a0397e40179ce62228303388dfda92a2ee91c1b6 --- /dev/null +++ b/agent/plugin/probe.h @@ -0,0 +1,55 @@ +#ifndef __PROBE_H +#define __PROBE_H + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +#define SK(sk) ((const struct sock *)(sk)) +#define NS_TIME() (bpf_ktime_get_ns() / 1000) +#define _R(dst, src) BPF_CORE_READ(dst, src) +#define IPV6_LEN 16 +#define MAX_COMM 16 +#define MAX_PACKET 1000 +#define AF_INET 2 +#define BPF_MAP_TYPE_PERCPU_COUNTER 10 +#define PID 32 +#define MAX 256 +#define MAX_SUM 1024 +#define TASK_COMM_LEN 16 + +/* bpf.h struct helper */ + +#define TCP 1 +#define UDP 2 +struct ktime_info +{ + u64 qdisc_time; + u64 mac_time; + u64 ip_time; + u64 tcp_time; + u64 tran_time; + u64 app_time; +}; + +struct event +{ + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u32 client_ip; + u32 server_ip; + u16 client_port; + u16 server_port; + u32 seq; + u32 ack; + u32 tran_flag; + u32 len; + int pid; + int udp_direction; + u32 protocol; + int tran_time; + char comm[TASK_COMM_LEN]; +}; + +#endif diff --git a/agent/plugin/traffic.bpf.h b/agent/plugin/traffic.bpf.h new file mode 100644 index 0000000000000000000000000000000000000000..64f4471718d2c3d1967980286c47a171b4ba064f --- /dev/null +++ b/agent/plugin/traffic.bpf.h @@ -0,0 +1,109 @@ +#include "common.bpf.h" +// udp +static __always_inline struct event get_packet_tuple(struct sk_buff *skb) +{ + struct event pkt_tuple = {0}; + struct iphdr *ip = extract_iphdr(skb); + struct udphdr *udp = extract_udphdr(skb); + + if (_R(ip, protocol) != IPPROTO_UDP) + { + return pkt_tuple; + } + get_udp_pkt_tuple(&pkt_tuple, ip, udp); + return pkt_tuple; +} + +static __always_inline struct ktime_info *loit(struct event *pkt_tuple) +{ + struct ktime_info zero = {0}; + return (struct ktime_info *)bmloti(&udp_map, pkt_tuple, &zero); +} + +static __always_inline void submit_message(struct event *pkt_tuple, u64 tran_time, u8 rx, u16 len) +{ + u32 ptid = get_current_tgid(); + struct event *message = bpf_ringbuf_reserve(&rb, sizeof(*message), 0); + if (!message) + return; + + message->tran_time = tran_time; + message->client_ip = pkt_tuple->client_ip; + message->server_ip = pkt_tuple->server_ip; + message->client_port = pkt_tuple->client_port; + message->server_port = pkt_tuple->server_port; + message->udp_direction = rx; + message->len = len; + message->pid = ptid; + bpf_get_current_comm(message->comm, sizeof(message->comm)); + bpf_ringbuf_submit(message, 0); +} + +static __always_inline int __udp_rcv(struct sk_buff *skb) +{ + if (skb == NULL) + return 0; + struct event pkt_tuple = get_packet_tuple(skb); + struct ktime_info *tinfo = loit(&pkt_tuple); + if (!tinfo) + return 0; + + tinfo->tran_time = NS_TIME(); + return 0; +} + +static __always_inline int udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) +{ + if (skb == NULL) + return 0; + struct event pkt_tuple = get_packet_tuple(skb); + struct ktime_info *tinfo = bpf_map_lookup_elem(&udp_map, &pkt_tuple); + if (!tinfo) + return 0; + + u64 tran_time = NS_TIME() - tinfo->tran_time; + + struct udphdr *udp = extract_udphdr(skb); + + submit_message(&pkt_tuple, tran_time, 1, __bpf_ntohs(_R(udp, len))); + return 0; +} + +static __always_inline int __udp_send_skb(struct sk_buff *skb) +{ + if (skb == NULL) + return 0; + + struct event pkt_tuple = {0}; + struct sock *sk = _R(skb, sk); + pkt_tuple.client_ip = _R(sk, __sk_common.skc_rcv_saddr); + pkt_tuple.server_ip = _R(sk, __sk_common.skc_daddr); + pkt_tuple.client_port = _R(sk, __sk_common.skc_num); + pkt_tuple.server_port = __bpf_ntohs(_R(sk, __sk_common.skc_dport)); + pkt_tuple.tran_flag = UDP; + + struct ktime_info *tinfo = loit(&pkt_tuple); + if (!tinfo) + return 0; + + tinfo->tran_time = NS_TIME(); + return 0; +} + +static __always_inline int __ip_send_skb(struct sk_buff *skb) +{ + if (skb == NULL) + return 0; + + struct event pkt_tuple = get_packet_tuple(skb); + + struct ktime_info *tinfo = bpf_map_lookup_elem(&udp_map, &pkt_tuple); + if (!tinfo || tinfo->tran_time == 0) + return 0; + + struct udphdr *udp = extract_udphdr(skb); + + u64 tran_time = NS_TIME() - tinfo->tran_time; + submit_message(&pkt_tuple, tran_time, 0, __bpf_ntohs(_R(udp, len))); + return 0; +}