├── .gitignore ├── tools ├── gen_vmlinux_h.sh └── cmake │ ├── FindLibBpf.cmake │ └── FindBpfObject.cmake ├── src ├── iptables_test.h ├── test_sockhash_kern.c ├── test_sockmap_kern.c ├── helpers │ ├── errno_helpers.h │ ├── syscall_helpers.h │ ├── map_helpers.h │ ├── compat.bpf.h │ ├── compat.h │ ├── map_helpers.c │ ├── compat.c │ └── errno_helpers.c ├── softirqs.h ├── runqlen.h ├── btf_helpers.h ├── netfilter_link_attach.bpf.c ├── sockfilter.h ├── xdp.bpf.c ├── minimal.bpf.c ├── test_cgroup_link.c ├── profile.h ├── data_breakpoint.h ├── test_lirc_mode2.bpf.c ├── uprobe_helpers.h ├── bits.bpf.h ├── fentry.bpf.c ├── with_tunnels.sh ├── kprobe.bpf.c ├── usdt.bpf.c ├── tcx.bpf.c ├── test_lirc_mode2.sh ├── net_shared.h ├── uprobe.bpf.c ├── tc.bpf.c ├── ksyscall.bpf.c ├── iptables_test.bpf.c ├── data_breakpoint.bpf.c ├── profile.bpf.c ├── lsm_connect.bpf.c ├── kprobe_multi.bpf.c ├── ksyscall.c ├── with_addr.sh ├── tcx.c ├── maps.bpf.h ├── minimal.c ├── fentry.c ├── kprobe.c ├── lsm_connect.c ├── kprobe_multi.c ├── xdp.c ├── flow_dissector_load.h ├── sockfilter.bpf.c ├── softirqs.bpf.c ├── runqlen.bpf.c ├── usdt.c ├── bpf_iter.bpf.c ├── tc.c ├── bpf_iter.c ├── flow_dissector_load.c ├── uprobe_multi.bpf.c ├── test_tc_link.c ├── trace_helpers.h ├── CMakeLists.txt ├── uprobe.c ├── netfilter_link_attach.c ├── Makefile ├── test_lirc_mode2.c ├── uprobe_multi.c ├── sockfilter.c ├── btf_helpers.c ├── test_flow_dissector.sh ├── test_lwt_bpf.c ├── softirqs.c ├── iptables_test.c ├── uprobe_helpers.c ├── bpf_dctcp.c ├── profile.c ├── runqlen.c ├── cgroup_link.c └── data_breakpoint.c ├── .gitmodules ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | [Bb]uild/ 2 | .vscode/ 3 | .output/ 4 | bin/ 5 | -------------------------------------------------------------------------------- /tools/gen_vmlinux_h.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | $(dirname "$0")/bpftool btf dump file ${1:-/sys/kernel/btf/vmlinux} format c 4 | -------------------------------------------------------------------------------- /src/iptables_test.h: -------------------------------------------------------------------------------- 1 | #ifndef __IPTABLE_TEST_H 2 | #define __IPTABLE_TEST_H 3 | 4 | struct stats { 5 | uint32_t uid; 6 | uint32_t _res; 7 | uint64_t packets; 8 | uint64_t bytes; 9 | }; 10 | 11 | #endif -------------------------------------------------------------------------------- /src/test_sockhash_kern.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2018 Covalent IO, Inc. http://covalent.io 3 | #undef SOCKMAP 4 | #define TEST_MAP_TYPE BPF_MAP_TYPE_SOCKHASH 5 | #include "./test_sockmap_kern.h" 6 | -------------------------------------------------------------------------------- /src/test_sockmap_kern.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2018 Covalent IO, Inc. http://covalent.io 3 | #define SOCKMAP 4 | #define TEST_MAP_TYPE BPF_MAP_TYPE_SOCKMAP 5 | #include "./test_sockmap_kern.h" 6 | -------------------------------------------------------------------------------- /src/helpers/errno_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __ERRNO_HELPERS_H 3 | #define __ERRNO_HELPERS_H 4 | 5 | int errno_by_name(const char *errno_name); 6 | 7 | #endif /* __ERRNO_HELPERS_H */ 8 | -------------------------------------------------------------------------------- /src/softirqs.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __SOFTIRQS_H 3 | #define __SOFTIRQS_H 4 | 5 | #define MAX_SLOTS 20 6 | 7 | struct hist { 8 | __u32 slots[MAX_SLOTS]; 9 | }; 10 | 11 | #endif /* __SOFTIRQS_H */ 12 | -------------------------------------------------------------------------------- /src/runqlen.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __RUNQLEN_H 3 | #define __RUNQLEN_H 4 | 5 | #define MAX_CPU_NR 128 6 | #define MAX_SLOTS 32 7 | 8 | struct hist { 9 | __u32 slots[MAX_SLOTS]; 10 | }; 11 | 12 | #endif /* __RUNQLEN_H */ 13 | -------------------------------------------------------------------------------- /src/btf_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | #ifndef __BTF_HELPERS_H 4 | #define __BTF_HELPERS_H 5 | 6 | #include 7 | 8 | int ensure_core_btf(struct bpf_object_open_opts *opts); 9 | void cleanup_core_btf(struct bpf_object_open_opts *opts); 10 | 11 | #endif /* __BTF_HELPERS_H */ 12 | -------------------------------------------------------------------------------- /src/netfilter_link_attach.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | 3 | #include "vmlinux.h" 4 | #include 5 | 6 | #define NF_ACCEPT 1 7 | 8 | SEC("netfilter") 9 | int nf_link_attach_test(struct bpf_nf_ctx *ctx) 10 | { 11 | return NF_ACCEPT; 12 | } 13 | 14 | char _license[] SEC("license") = "GPL"; 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf.git 4 | [submodule "bpftool"] 5 | path = bpftool 6 | url = https://github.com/libbpf/bpftool 7 | [submodule "blazesym"] 8 | path = blazesym 9 | url = https://github.com/libbpf/blazesym.git 10 | [submodule "vmlinux.h"] 11 | path = vmlinux.h 12 | url = https://github.com/libbpf/vmlinux.h.git -------------------------------------------------------------------------------- /src/helpers/syscall_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __SYSCALL_HELPERS_H 3 | #define __SYSCALL_HELPERS_H 4 | 5 | #include 6 | 7 | void init_syscall_names(void); 8 | void free_syscall_names(void); 9 | void list_syscalls(void); 10 | void syscall_name(unsigned n, char *buf, size_t size); 11 | 12 | #endif /* __SYSCALL_HELPERS_H */ 13 | -------------------------------------------------------------------------------- /src/helpers/map_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Anton Protopopov */ 3 | #ifndef __MAP_HELPERS_H 4 | #define __MAP_HELPERS_H 5 | 6 | #include 7 | 8 | int dump_hash(int map_fd, void *keys, __u32 key_size, 9 | void *values, __u32 value_size, __u32 *count, void *invalid_key); 10 | 11 | #endif /* __MAP_HELPERS_H */ 12 | -------------------------------------------------------------------------------- /src/sockfilter.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #ifndef __SOCKFILTER_H 4 | #define __SOCKFILTER_H 5 | 6 | struct so_event { 7 | __be32 src_addr; 8 | __be32 dst_addr; 9 | union { 10 | __be32 ports; 11 | __be16 port16[2]; 12 | }; 13 | __u32 ip_proto; 14 | __u32 pkt_type; 15 | __u32 ifindex; 16 | }; 17 | 18 | #endif /* __SOCKFILTER_H */ 19 | -------------------------------------------------------------------------------- /src/xdp.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | /// @ifindex 1 5 | /// @flags 0 6 | /// @xdpopts {"old_prog_fd":0} 7 | SEC("xdp") 8 | int xdp_pass(struct xdp_md* ctx) { 9 | void* data = (void*)(long)ctx->data; 10 | void* data_end = (void*)(long)ctx->data_end; 11 | int pkt_sz = data_end - data; 12 | 13 | bpf_printk("packet size is %d", pkt_sz); 14 | return XDP_PASS; 15 | } 16 | 17 | char __license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /src/minimal.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | 6 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 7 | 8 | int my_pid = 0; 9 | 10 | SEC("tp/syscalls/sys_enter_write") 11 | int handle_tp(void *ctx) 12 | { 13 | int pid = bpf_get_current_pid_tgid() >> 32; 14 | 15 | if (pid != my_pid) 16 | return 0; 17 | 18 | bpf_printk("BPF triggered from PID %d.\n", pid); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/test_cgroup_link.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Facebook 3 | #include 4 | #include 5 | 6 | int calls = 0; 7 | int alt_calls = 0; 8 | 9 | SEC("cgroup_skb/egress") 10 | int egress(struct __sk_buff *skb) 11 | { 12 | __sync_fetch_and_add(&calls, 1); 13 | return 1; 14 | } 15 | 16 | SEC("cgroup_skb/egress") 17 | int egress_alt(struct __sk_buff *skb) 18 | { 19 | __sync_fetch_and_add(&alt_calls, 1); 20 | return 1; 21 | } 22 | 23 | char _license[] SEC("license") = "GPL"; 24 | 25 | -------------------------------------------------------------------------------- /src/profile.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #ifndef __PROFILE_H_ 4 | #define __PROFILE_H_ 5 | 6 | #ifndef TASK_COMM_LEN 7 | #define TASK_COMM_LEN 16 8 | #endif 9 | 10 | #ifndef MAX_STACK_DEPTH 11 | #define MAX_STACK_DEPTH 128 12 | #endif 13 | 14 | typedef __u64 stack_trace_t[MAX_STACK_DEPTH]; 15 | 16 | struct stacktrace_event { 17 | __u32 pid; 18 | __u32 cpu_id; 19 | char comm[TASK_COMM_LEN]; 20 | __s32 kstack_sz; 21 | __s32 ustack_sz; 22 | stack_trace_t kstack; 23 | stack_trace_t ustack; 24 | }; 25 | 26 | #endif /* __PROFILE_H_ */ 27 | -------------------------------------------------------------------------------- /src/data_breakpoint.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #ifndef __DATA_BREAKPOINT_H_ 4 | #define __DATA_BREAKPOINT_H_ 5 | 6 | #ifndef TASK_COMM_LEN 7 | #define TASK_COMM_LEN 16 8 | #endif 9 | 10 | #ifndef MAX_STACK_DEPTH 11 | #define MAX_STACK_DEPTH 128 12 | #endif 13 | 14 | typedef __u64 stack_trace_t[MAX_STACK_DEPTH]; 15 | 16 | struct stacktrace_event { 17 | __u32 pid; 18 | __u32 cpu_id; 19 | char comm[TASK_COMM_LEN]; 20 | __s32 kstack_sz; 21 | __s32 ustack_sz; 22 | stack_trace_t kstack; 23 | stack_trace_t ustack; 24 | }; 25 | 26 | #endif /* __DATA_BREAKPOINT_H_ */ 27 | -------------------------------------------------------------------------------- /src/test_lirc_mode2.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // test ir decoder 3 | // 4 | // Copyright (C) 2018 Sean Young 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | SEC("lirc_mode2") 11 | int bpf_decoder(unsigned int *sample) 12 | { 13 | if (LIRC_IS_PULSE(*sample)) { 14 | unsigned int duration = LIRC_VALUE(*sample); 15 | 16 | if (duration & 0x10000) 17 | bpf_rc_keydown(sample, 0x40, duration & 0xffff, 0); 18 | if (duration & 0x20000) 19 | bpf_rc_pointer_rel(sample, (duration >> 8) & 0xff, 20 | duration & 0xff); 21 | } 22 | 23 | return 0; 24 | } 25 | 26 | char _license[] SEC("license") = "GPL"; 27 | -------------------------------------------------------------------------------- /src/uprobe_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2021 Google LLC. */ 3 | #ifndef __UPROBE_HELPERS_H 4 | #define __UPROBE_HELPERS_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int get_pid_binary_path(pid_t pid, char *path, size_t path_sz); 11 | int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz); 12 | int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz); 13 | off_t get_elf_func_offset(const char *path, const char *func); 14 | Elf *open_elf(const char *path, int *fd_close); 15 | Elf *open_elf_by_fd(int fd); 16 | void close_elf(Elf *e, int fd_close); 17 | 18 | #endif /* __UPROBE_HELPERS_H */ 19 | -------------------------------------------------------------------------------- /src/bits.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BITS_BPF_H 3 | #define __BITS_BPF_H 4 | 5 | #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 6 | #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) 7 | 8 | static __always_inline u64 log2(u32 v) 9 | { 10 | u32 shift, r; 11 | 12 | r = (v > 0xFFFF) << 4; v >>= r; 13 | shift = (v > 0xFF) << 3; v >>= shift; r |= shift; 14 | shift = (v > 0xF) << 2; v >>= shift; r |= shift; 15 | shift = (v > 0x3) << 1; v >>= shift; r |= shift; 16 | r |= (v >> 1); 17 | 18 | return r; 19 | } 20 | 21 | static __always_inline u64 log2l(u64 v) 22 | { 23 | u32 hi = v >> 32; 24 | 25 | if (hi) 26 | return log2(hi) + 32; 27 | else 28 | return log2(v); 29 | } 30 | 31 | #endif /* __BITS_BPF_H */ 32 | -------------------------------------------------------------------------------- /tools/cmake/FindLibBpf.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | find_path(LIBBPF_INCLUDE_DIRS 4 | NAMES 5 | bpf/bpf.h 6 | bpf/btf.h 7 | bpf/libbpf.h 8 | PATHS 9 | /usr/include 10 | /usr/local/include 11 | /opt/local/include 12 | /sw/include 13 | ENV CPATH) 14 | 15 | find_library(LIBBPF_LIBRARIES 16 | NAMES 17 | bpf 18 | PATHS 19 | /usr/lib 20 | /usr/local/lib 21 | /opt/local/lib 22 | /sw/lib 23 | ENV LIBRARY_PATH 24 | ENV LD_LIBRARY_PATH) 25 | 26 | include (FindPackageHandleStandardArgs) 27 | 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf "Please install the libbpf development package" 29 | LIBBPF_LIBRARIES 30 | LIBBPF_INCLUDE_DIRS) 31 | 32 | mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES) 33 | -------------------------------------------------------------------------------- /src/fentry.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | 7 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 8 | 9 | SEC("fentry/do_unlinkat") 10 | int BPF_PROG(do_unlinkat, int dfd, struct filename *name) 11 | { 12 | pid_t pid; 13 | 14 | pid = bpf_get_current_pid_tgid() >> 32; 15 | bpf_printk("fentry: pid = %d, filename = %s\n", pid, name->name); 16 | return 0; 17 | } 18 | 19 | SEC("fexit/do_unlinkat") 20 | int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret) 21 | { 22 | pid_t pid; 23 | 24 | pid = bpf_get_current_pid_tgid() >> 32; 25 | bpf_printk("fexit: pid = %d, filename = %s, ret = %ld\n", pid, name->name, ret); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/with_tunnels.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # setup tunnels for flow dissection test 5 | 6 | readonly SUFFIX="test_$(mktemp -u XXXX)" 7 | CONFIG="remote 127.0.0.2 local 127.0.0.1 dev lo" 8 | 9 | setup() { 10 | ip link add "ipip_${SUFFIX}" type ipip ${CONFIG} 11 | ip link add "gre_${SUFFIX}" type gre ${CONFIG} 12 | ip link add "sit_${SUFFIX}" type sit ${CONFIG} 13 | 14 | echo "tunnels before test:" 15 | ip tunnel show 16 | 17 | ip link set "ipip_${SUFFIX}" up 18 | ip link set "gre_${SUFFIX}" up 19 | ip link set "sit_${SUFFIX}" up 20 | } 21 | 22 | 23 | cleanup() { 24 | ip tunnel del "ipip_${SUFFIX}" 25 | ip tunnel del "gre_${SUFFIX}" 26 | ip tunnel del "sit_${SUFFIX}" 27 | 28 | echo "tunnels after test:" 29 | ip tunnel show 30 | } 31 | 32 | trap cleanup EXIT 33 | 34 | setup 35 | "$@" 36 | exit "$?" 37 | -------------------------------------------------------------------------------- /src/kprobe.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | SEC("kprobe/do_unlinkat") 11 | int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name) 12 | { 13 | pid_t pid; 14 | const char *filename; 15 | 16 | pid = bpf_get_current_pid_tgid() >> 32; 17 | filename = BPF_CORE_READ(name, name); 18 | bpf_printk("KPROBE ENTRY pid = %d, filename = %s\n", pid, filename); 19 | return 0; 20 | } 21 | 22 | SEC("kretprobe/do_unlinkat") 23 | int BPF_KRETPROBE(do_unlinkat_exit, long ret) 24 | { 25 | pid_t pid; 26 | 27 | pid = bpf_get_current_pid_tgid() >> 32; 28 | bpf_printk("KPROBE EXIT: pid = %d, ret = %ld\n", pid, ret); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/usdt.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | pid_t my_pid; 9 | 10 | SEC("usdt/libc.so.6:libc:setjmp") 11 | int BPF_USDT(usdt_auto_attach, void *arg1, int arg2, void *arg3) 12 | { 13 | pid_t pid = bpf_get_current_pid_tgid() >> 32; 14 | 15 | if (pid != my_pid) 16 | return 0; 17 | 18 | bpf_printk("USDT auto attach to libc:setjmp: arg1 = %lx, arg2 = %d, arg3 = %lx", arg1, arg2, arg3); 19 | return 0; 20 | } 21 | 22 | SEC("usdt") 23 | int BPF_USDT(usdt_manual_attach, void *arg1, int arg2, void *arg3) 24 | { 25 | bpf_printk("USDT manual attach to libc:setjmp: arg1 = %lx, arg2 = %d, arg3 = %lx", arg1, arg2, arg3); 26 | return 0; 27 | } 28 | 29 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 30 | -------------------------------------------------------------------------------- /src/tcx.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 9 | 10 | SEC("tcx/ingress") 11 | int tc_ingress(struct __sk_buff *ctx) 12 | { 13 | void *data_end = (void *)(__u64)ctx->data_end; 14 | void *data = (void *)(__u64)ctx->data; 15 | struct ethhdr *l2; 16 | struct iphdr *l3; 17 | 18 | if (ctx->protocol != bpf_htons(ETH_P_IP)) 19 | return TCX_PASS; 20 | 21 | l2 = data; 22 | if ((void *)(l2 + 1) > data_end) 23 | return TCX_PASS; 24 | 25 | l3 = (struct iphdr *)(l2 + 1); 26 | if ((void *)(l3 + 1) > data_end) 27 | return TCX_PASS; 28 | 29 | bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), l3->ttl); 30 | return TCX_PASS; 31 | } 32 | 33 | char __license[] SEC("license") = "GPL"; 34 | -------------------------------------------------------------------------------- /src/test_lirc_mode2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | 4 | # Kselftest framework requirement - SKIP code is 4. 5 | ksft_skip=4 6 | ret=$ksft_skip 7 | 8 | msg="skip all tests:" 9 | if [ $UID != 0 ]; then 10 | echo $msg please run this as root >&2 11 | exit $ksft_skip 12 | fi 13 | 14 | GREEN='\033[0;92m' 15 | RED='\033[0;31m' 16 | NC='\033[0m' # No Color 17 | 18 | modprobe rc-loopback 19 | 20 | for i in /sys/class/rc/rc* 21 | do 22 | if grep -q DRV_NAME=rc-loopback $i/uevent 23 | then 24 | LIRCDEV=$(grep DEVNAME= $i/lirc*/uevent | sed sQDEVNAME=Q/dev/Q) 25 | INPUTDEV=$(grep DEVNAME= $i/input*/event*/uevent | sed sQDEVNAME=Q/dev/Q) 26 | fi 27 | done 28 | 29 | if [ -n "$LIRCDEV" ]; 30 | then 31 | TYPE=lirc_mode2 32 | ./test_lirc_mode2 $LIRCDEV $INPUTDEV 33 | ret=$? 34 | if [ $ret -ne 0 ]; then 35 | echo -e ${RED}"FAIL: $TYPE"${NC} 36 | else 37 | echo -e ${GREEN}"PASS: $TYPE"${NC} 38 | fi 39 | fi 40 | 41 | exit $ret 42 | -------------------------------------------------------------------------------- /src/net_shared.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | #ifndef _NET_SHARED_H 3 | #define _NET_SHARED_H 4 | 5 | #define AF_INET 2 6 | #define AF_INET6 10 7 | 8 | #define ETH_ALEN 6 9 | #define ETH_P_802_3_MIN 0x0600 10 | #define ETH_P_8021Q 0x8100 11 | #define ETH_P_8021AD 0x88A8 12 | #define ETH_P_IP 0x0800 13 | #define ETH_P_IPV6 0x86DD 14 | #define ETH_P_ARP 0x0806 15 | #define IPPROTO_ICMPV6 58 16 | 17 | #define TC_ACT_OK 0 18 | #define TC_ACT_SHOT 2 19 | 20 | #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ 21 | __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 22 | #define bpf_ntohs(x) __builtin_bswap16(x) 23 | #define bpf_htons(x) __builtin_bswap16(x) 24 | #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ 25 | __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 26 | #define bpf_ntohs(x) (x) 27 | #define bpf_htons(x) (x) 28 | #else 29 | # error "Endianness detection needs to be set up for your compiler?!" 30 | #endif 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/uprobe.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | SEC("uprobe") 11 | int BPF_KPROBE(uprobe_add, int a, int b) 12 | { 13 | bpf_printk("uprobed_add ENTRY: a = %d, b = %d", a, b); 14 | return 0; 15 | } 16 | 17 | SEC("uretprobe") 18 | int BPF_KRETPROBE(uretprobe_add, int ret) 19 | { 20 | bpf_printk("uprobed_add EXIT: return = %d", ret); 21 | return 0; 22 | } 23 | 24 | SEC("uprobe//proc/self/exe:uprobed_sub") 25 | int BPF_KPROBE(uprobe_sub, int a, int b) 26 | { 27 | bpf_printk("uprobed_sub ENTRY: a = %d, b = %d", a, b); 28 | return 0; 29 | } 30 | 31 | SEC("uretprobe//proc/self/exe:uprobed_sub") 32 | int BPF_KRETPROBE(uretprobe_sub, int ret) 33 | { 34 | bpf_printk("uprobed_sub EXIT: return = %d", ret); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/tc.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define TC_ACT_OK 0 9 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 10 | 11 | SEC("tc") 12 | int tc_ingress(struct __sk_buff *ctx) 13 | { 14 | void *data_end = (void *)(__u64)ctx->data_end; 15 | void *data = (void *)(__u64)ctx->data; 16 | struct ethhdr *l2; 17 | struct iphdr *l3; 18 | 19 | if (ctx->protocol != bpf_htons(ETH_P_IP)) 20 | return TC_ACT_OK; 21 | 22 | l2 = data; 23 | if ((void *)(l2 + 1) > data_end) 24 | return TC_ACT_OK; 25 | 26 | l3 = (struct iphdr *)(l2 + 1); 27 | if ((void *)(l3 + 1) > data_end) 28 | return TC_ACT_OK; 29 | 30 | bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), l3->ttl); 31 | return TC_ACT_OK; 32 | } 33 | 34 | char __license[] SEC("license") = "GPL"; 35 | -------------------------------------------------------------------------------- /src/ksyscall.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define TASK_COMM_LEN 16 7 | 8 | SEC("ksyscall/unlinkat") 9 | int BPF_KSYSCALL(unlinkat_entry, int fd, const char *pathname, int flag) 10 | { 11 | char comm[TASK_COMM_LEN]; 12 | __u32 caller_pid = bpf_get_current_pid_tgid() >> 32; 13 | 14 | bpf_get_current_comm(&comm, sizeof(comm)); 15 | bpf_printk( "PID %d (%s) unlinkat syscall called with fd[%d], pathname[%s] and flag[%d].", 16 | caller_pid, comm, fd, pathname, flag); 17 | return 0; 18 | } 19 | 20 | SEC("kretsyscall/unlinkat") 21 | int BPF_KRETPROBE(unlinkat_return, int ret) 22 | { 23 | char comm[TASK_COMM_LEN]; 24 | __u32 caller_pid = bpf_get_current_pid_tgid() >> 32; 25 | 26 | bpf_get_current_comm(&comm, sizeof(comm)); 27 | bpf_printk("PID %d (%s) unlinkat syscall return called with ret[%d].", caller_pid, comm, ret); 28 | return 0; 29 | } 30 | 31 | char _license[] SEC("license") = "GPL"; 32 | -------------------------------------------------------------------------------- /src/iptables_test.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | #include "iptables_test.h" 6 | 7 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 8 | 9 | struct 10 | { 11 | __uint(type, BPF_MAP_TYPE_HASH); 12 | __uint(max_entries, 100); 13 | __type(key, uint32_t); 14 | __type(value, struct stats); 15 | } cookie_stats SEC(".maps"); 16 | 17 | SEC("socket") 18 | int iptables_accepted(struct __sk_buff *skb) 19 | { 20 | uint32_t cookie = bpf_get_socket_cookie(skb); 21 | struct stats *rst = bpf_map_lookup_elem(&cookie_stats, &cookie); 22 | if (rst == NULL) 23 | { 24 | struct stats stat; 25 | stat.uid = bpf_get_socket_uid(skb); 26 | stat._res = 0; 27 | stat.packets = 1; 28 | stat.bytes = skb->len; 29 | bpf_map_update_elem(&cookie_stats, &cookie, &stat, BPF_ANY); 30 | } 31 | else 32 | { 33 | rst->uid = bpf_get_socket_uid(skb); 34 | rst->packets += 1; 35 | rst->bytes += skb->len; 36 | } 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/data_breakpoint.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #include "data_breakpoint.h" 9 | 10 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 11 | 12 | struct 13 | { 14 | __uint(type, BPF_MAP_TYPE_RINGBUF); 15 | __uint(max_entries, 256 * 1024); 16 | } events SEC(".maps"); 17 | 18 | SEC("perf_event") 19 | int profile(void *ctx) 20 | { 21 | struct stacktrace_event *event; 22 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 23 | if (!event) 24 | return 1; 25 | 26 | event->pid = bpf_get_current_pid_tgid() >> 32; 27 | event->cpu_id = bpf_get_smp_processor_id(); 28 | 29 | if (bpf_get_current_comm(event->comm, sizeof(event->comm))) 30 | event->comm[0] = 0; 31 | 32 | event->kstack_sz = bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); 33 | event->ustack_sz = bpf_get_stack(ctx, event->ustack, sizeof(event->ustack), BPF_F_USER_STACK); 34 | bpf_ringbuf_submit(event, 0); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/profile.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #include "profile.h" 9 | 10 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 11 | 12 | struct { 13 | __uint(type, BPF_MAP_TYPE_RINGBUF); 14 | __uint(max_entries, 256 * 1024); 15 | } events SEC(".maps"); 16 | 17 | SEC("perf_event") 18 | int profile(void *ctx) 19 | { 20 | int pid = bpf_get_current_pid_tgid() >> 32; 21 | int cpu_id = bpf_get_smp_processor_id(); 22 | struct stacktrace_event *event; 23 | int cp; 24 | 25 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 26 | if (!event) 27 | return 1; 28 | 29 | event->pid = pid; 30 | event->cpu_id = cpu_id; 31 | 32 | if (bpf_get_current_comm(event->comm, sizeof(event->comm))) 33 | event->comm[0] = 0; 34 | 35 | event->kstack_sz = bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); 36 | 37 | event->ustack_sz = 38 | bpf_get_stack(ctx, event->ustack, sizeof(event->ustack), BPF_F_USER_STACK); 39 | 40 | bpf_ringbuf_submit(event, 0); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/lsm_connect.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | 6 | char LICENSE[] SEC("license") = "GPL"; 7 | 8 | #define EPERM 1 9 | #define AF_INET 2 10 | 11 | const __u32 blockme = 0x01010101; // 1.1.1.1 12 | 13 | SEC("lsm/socket_connect") 14 | int BPF_PROG(restrict_connect, struct socket *sock, struct sockaddr *address, int addrlen, int ret) 15 | { 16 | // Satisfying "cannot override a denial" rule 17 | if (ret != 0) 18 | return ret; 19 | 20 | // Only IPv4 in this example 21 | if (address->sa_family != AF_INET) 22 | return 0; 23 | 24 | // Cast the address to an IPv4 socket address 25 | struct sockaddr_in *addr = (struct sockaddr_in *)address; 26 | 27 | // Where do you want to go? 28 | __u32 dest = addr->sin_addr.s_addr; 29 | bpf_printk("lsm: found connect to %08x", dest); 30 | 31 | if (dest == blockme) 32 | { 33 | bpf_printk("lsm: blocking %08x", dest); 34 | return -EPERM; 35 | } 36 | return 0; 37 | } 38 | 39 | SEC("lsm/sk_free_security") 40 | void BPF_PROG(sk_free_security, struct socket *sock) 41 | { 42 | // do nothing 43 | } 44 | -------------------------------------------------------------------------------- /src/helpers/compat.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | 4 | #ifndef __COMPAT_BPF_H 5 | #define __COMPAT_BPF_H 6 | 7 | #include 8 | #include 9 | 10 | #define MAX_EVENT_SIZE 10240 11 | #define RINGBUF_SIZE (1024 * 256) 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 15 | __uint(max_entries, 1); 16 | __uint(key_size, sizeof(__u32)); 17 | __uint(value_size, MAX_EVENT_SIZE); 18 | } heap SEC(".maps"); 19 | 20 | struct { 21 | __uint(type, BPF_MAP_TYPE_RINGBUF); 22 | __uint(max_entries, RINGBUF_SIZE); 23 | } events SEC(".maps"); 24 | 25 | static __always_inline void *reserve_buf(__u64 size) 26 | { 27 | static const int zero = 0; 28 | 29 | if (bpf_core_type_exists(struct bpf_ringbuf)) 30 | return bpf_ringbuf_reserve(&events, size, 0); 31 | 32 | return bpf_map_lookup_elem(&heap, &zero); 33 | } 34 | 35 | static __always_inline long submit_buf(void *ctx, void *buf, __u64 size) 36 | { 37 | if (bpf_core_type_exists(struct bpf_ringbuf)) { 38 | bpf_ringbuf_submit(buf, 0); 39 | return 0; 40 | } 41 | 42 | return bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, buf, size); 43 | } 44 | 45 | #endif /* __COMPAT_BPF_H */ 46 | -------------------------------------------------------------------------------- /src/kprobe_multi.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | SEC("kprobe.multi/do_unlinkat") 11 | int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name) 12 | { 13 | pid_t pid; 14 | const char *filename; 15 | pid = bpf_get_current_pid_tgid() >> 32; 16 | filename = BPF_CORE_READ(name, name); 17 | bpf_printk("KPROBE.MULTI ENTRY pid = %d, filename = %s\n", pid, filename); 18 | return 0; 19 | } 20 | 21 | SEC("kretprobe.multi/do_unlinkat") 22 | int BPF_KRETPROBE(do_unlinkat_exit, long ret) 23 | { 24 | pid_t pid; 25 | pid = bpf_get_current_pid_tgid() >> 32; 26 | bpf_printk("KPROBE.MULTI EXIT: pid = %d, ret = %ld\n", pid, ret); 27 | return 0; 28 | } 29 | 30 | SEC("kprobe.session/do_unlinkat") 31 | int BPF_KPROBE(session_unlinkat, int dfd, struct filename *name) 32 | { 33 | pid_t pid = bpf_get_current_pid_tgid() >> 32; 34 | const char *filename = BPF_CORE_READ(name, name); 35 | bool is_return = bpf_session_is_return(); 36 | bpf_printk("KPROBE.SESSION %s pid = %d, filename = %s\n", is_return ? "EXIT" : "ENTRY", pid, filename); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /src/helpers/compat.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | 4 | #ifndef __COMPAT_H 5 | #define __COMPAT_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define POLL_TIMEOUT_MS 100 13 | 14 | struct bpf_buffer; 15 | struct bpf_map; 16 | 17 | typedef int (*bpf_buffer_sample_fn)(void *ctx, void *data, size_t size); 18 | typedef void (*bpf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt); 19 | 20 | struct bpf_buffer *bpf_buffer__new(struct bpf_map *events, struct bpf_map *heap); 21 | int bpf_buffer__open(struct bpf_buffer *buffer, bpf_buffer_sample_fn sample_cb, 22 | bpf_buffer_lost_fn lost_cb, void *ctx); 23 | int bpf_buffer__poll(struct bpf_buffer *, int timeout_ms); 24 | void bpf_buffer__free(struct bpf_buffer *); 25 | 26 | /* taken from libbpf */ 27 | #ifndef __has_builtin 28 | #define __has_builtin(x) 0 29 | #endif 30 | 31 | static inline void *libbpf_reallocarray(void *ptr, size_t nmemb, size_t size) 32 | { 33 | size_t total; 34 | 35 | #if __has_builtin(__builtin_mul_overflow) 36 | if (__builtin_mul_overflow(nmemb, size, &total)) 37 | return NULL; 38 | #else 39 | if (size == 0 || nmemb > ULONG_MAX / size) 40 | return NULL; 41 | total = nmemb * size; 42 | #endif 43 | return realloc(ptr, total); 44 | } 45 | 46 | #endif /* __COMPAT_H */ 47 | -------------------------------------------------------------------------------- /src/ksyscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ksyscall.skel.h" 9 | 10 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 11 | { 12 | return vfprintf(stderr, format, args); 13 | } 14 | 15 | static volatile sig_atomic_t stop; 16 | 17 | static void sig_int(int signo) 18 | { 19 | stop = 1; 20 | } 21 | 22 | int main(int argc, char **argv) 23 | { 24 | struct ksyscall_bpf *skel; 25 | int err; 26 | 27 | /* Set up libbpf errors and debug info callback */ 28 | libbpf_set_print(libbpf_print_fn); 29 | 30 | /* Open load and verify BPF application */ 31 | skel = ksyscall_bpf__open_and_load(); 32 | if (!skel) { 33 | fprintf(stderr, "Failed to open BPF skeleton\n"); 34 | return 1; 35 | } 36 | 37 | /* Attach tracepoint handler */ 38 | err = ksyscall_bpf__attach(skel); 39 | if (err) { 40 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 41 | goto cleanup; 42 | } 43 | 44 | if (signal(SIGINT, sig_int) == SIG_ERR) { 45 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 46 | goto cleanup; 47 | } 48 | 49 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 50 | "to see output of the BPF programs.\n"); 51 | 52 | while (!stop) { 53 | fprintf(stderr, "."); 54 | sleep(1); 55 | } 56 | 57 | cleanup: 58 | ksyscall_bpf__destroy(skel); 59 | return -err; 60 | } 61 | -------------------------------------------------------------------------------- /src/with_addr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # add private ipv4 and ipv6 addresses to loopback 5 | 6 | readonly V6_INNER='100::a/128' 7 | readonly V4_INNER='192.168.0.1/32' 8 | 9 | if getopts ":s" opt; then 10 | readonly SIT_DEV_NAME='sixtofourtest0' 11 | readonly V6_SIT='2::/64' 12 | readonly V4_SIT='172.17.0.1/32' 13 | shift 14 | fi 15 | 16 | fail() { 17 | echo "error: $*" 1>&2 18 | exit 1 19 | } 20 | 21 | setup() { 22 | ip -6 addr add "${V6_INNER}" dev lo || fail 'failed to setup v6 address' 23 | ip -4 addr add "${V4_INNER}" dev lo || fail 'failed to setup v4 address' 24 | 25 | if [[ -n "${V6_SIT}" ]]; then 26 | ip link add "${SIT_DEV_NAME}" type sit remote any local any \ 27 | || fail 'failed to add sit' 28 | ip link set dev "${SIT_DEV_NAME}" up \ 29 | || fail 'failed to bring sit device up' 30 | ip -6 addr add "${V6_SIT}" dev "${SIT_DEV_NAME}" \ 31 | || fail 'failed to setup v6 SIT address' 32 | ip -4 addr add "${V4_SIT}" dev "${SIT_DEV_NAME}" \ 33 | || fail 'failed to setup v4 SIT address' 34 | fi 35 | 36 | sleep 2 # avoid race causing bind to fail 37 | } 38 | 39 | cleanup() { 40 | if [[ -n "${V6_SIT}" ]]; then 41 | ip -4 addr del "${V4_SIT}" dev "${SIT_DEV_NAME}" 42 | ip -6 addr del "${V6_SIT}" dev "${SIT_DEV_NAME}" 43 | ip link del "${SIT_DEV_NAME}" 44 | fi 45 | 46 | ip -4 addr del "${V4_INNER}" dev lo 47 | ip -6 addr del "${V6_INNER}" dev lo 48 | } 49 | 50 | trap cleanup EXIT 51 | 52 | setup 53 | "$@" 54 | exit "$?" 55 | -------------------------------------------------------------------------------- /src/tcx.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include "tcx.skel.h" 7 | 8 | #define LO_IFINDEX 3 9 | 10 | static volatile sig_atomic_t exiting = 0; 11 | 12 | static void sig_int(int signo) 13 | { 14 | exiting = 1; 15 | } 16 | 17 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 18 | { 19 | return vfprintf(stderr, format, args); 20 | } 21 | 22 | int main(int argc, char **argv) 23 | { 24 | LIBBPF_OPTS(bpf_tcx_opts, optl); 25 | struct tcx_bpf *skel; 26 | int err; 27 | 28 | libbpf_set_print(libbpf_print_fn); 29 | 30 | skel = tcx_bpf__open_and_load(); 31 | if (!skel) { 32 | fprintf(stderr, "Failed to open BPF skeleton\n"); 33 | return 1; 34 | } 35 | skel->links.tc_ingress = bpf_program__attach_tcx(skel->progs.tc_ingress, LO_IFINDEX, &optl); 36 | if ((err = libbpf_get_error(skel->links.tc_ingress)) != 0) 37 | { 38 | fprintf(stderr, "Failed to attach TC: %d\n", err); 39 | goto cleanup; 40 | } 41 | 42 | if (signal(SIGINT, sig_int) == SIG_ERR) { 43 | err = errno; 44 | fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno)); 45 | goto cleanup; 46 | } 47 | 48 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 49 | "to see output of the BPF program.\n"); 50 | 51 | while (!exiting) { 52 | fprintf(stderr, "."); 53 | sleep(1); 54 | } 55 | 56 | cleanup: 57 | tcx_bpf__destroy(skel); 58 | return -err; 59 | } 60 | -------------------------------------------------------------------------------- /src/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void * 10 | bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) 11 | { 12 | void *val; 13 | /* bpf helper functions like bpf_map_update_elem() below normally return 14 | * long, but using int instead of long to store the result is a workaround 15 | * to avoid incorrectly evaluating err in cases where the following criteria 16 | * is met: 17 | * the architecture is 64-bit 18 | * the helper function return type is long 19 | * the helper function returns the value of a call to a bpf_map_ops func 20 | * the bpf_map_ops function return type is int 21 | * the compiler inlines the helper function 22 | * the compiler does not sign extend the result of the bpf_map_ops func 23 | * 24 | * if this criteria is met, at best an error can only be checked as zero or 25 | * non-zero. it will not be possible to check for a negative value or a 26 | * specific error value. this is because the sign bit would have been stuck 27 | * at the 32nd bit of a 64-bit long int. 28 | */ 29 | int err; 30 | 31 | val = bpf_map_lookup_elem(map, key); 32 | if (val) 33 | return val; 34 | 35 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 36 | if (err && err != -EEXIST) 37 | return 0; 38 | 39 | return bpf_map_lookup_elem(map, key); 40 | } 41 | 42 | #endif /* __MAPS_BPF_H */ 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, mannkafai 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/minimal.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "minimal.skel.h" 8 | 9 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 10 | { 11 | return vfprintf(stderr, format, args); 12 | } 13 | 14 | int main(int argc, char **argv) 15 | { 16 | struct minimal_bpf *skel; 17 | int err; 18 | 19 | /* Set up libbpf errors and debug info callback */ 20 | libbpf_set_print(libbpf_print_fn); 21 | 22 | /* Open BPF application */ 23 | skel = minimal_bpf__open(); 24 | if (!skel) { 25 | fprintf(stderr, "Failed to open BPF skeleton\n"); 26 | return 1; 27 | } 28 | 29 | /* ensure BPF program only handles write() syscalls from our process */ 30 | skel->bss->my_pid = getpid(); 31 | 32 | /* Load & verify BPF programs */ 33 | err = minimal_bpf__load(skel); 34 | if (err) { 35 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 36 | goto cleanup; 37 | } 38 | 39 | /* Attach tracepoint handler */ 40 | err = minimal_bpf__attach(skel); 41 | if (err) { 42 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 43 | goto cleanup; 44 | } 45 | 46 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 47 | "to see output of the BPF programs.\n"); 48 | 49 | for (;;) { 50 | /* trigger our BPF program */ 51 | fprintf(stderr, "."); 52 | sleep(1); 53 | } 54 | 55 | cleanup: 56 | minimal_bpf__destroy(skel); 57 | return -err; 58 | } 59 | -------------------------------------------------------------------------------- /src/fentry.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fentry.skel.h" 13 | 14 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 15 | { 16 | return vfprintf(stderr, format, args); 17 | } 18 | 19 | static volatile sig_atomic_t stop; 20 | 21 | void sig_int(int signo) 22 | { 23 | stop = 1; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | struct fentry_bpf *skel; 29 | int err; 30 | 31 | /* Set up libbpf errors and debug info callback */ 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | /* Open load and verify BPF application */ 35 | skel = fentry_bpf__open_and_load(); 36 | if (!skel) { 37 | fprintf(stderr, "Failed to open BPF skeleton\n"); 38 | return 1; 39 | } 40 | 41 | /* Attach tracepoint handler */ 42 | err = fentry_bpf__attach(skel); 43 | if (err) { 44 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 45 | goto cleanup; 46 | } 47 | 48 | if (signal(SIGINT, sig_int) == SIG_ERR) { 49 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | while (!stop) { 57 | fprintf(stderr, "."); 58 | sleep(1); 59 | } 60 | 61 | cleanup: 62 | fentry_bpf__destroy(skel); 63 | return -err; 64 | } 65 | -------------------------------------------------------------------------------- /src/kprobe.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "kprobe.skel.h" 13 | 14 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 15 | { 16 | return vfprintf(stderr, format, args); 17 | } 18 | 19 | static volatile sig_atomic_t stop; 20 | 21 | static void sig_int(int signo) 22 | { 23 | stop = 1; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | struct kprobe_bpf *skel; 29 | int err; 30 | 31 | /* Set up libbpf errors and debug info callback */ 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | /* Open load and verify BPF application */ 35 | skel = kprobe_bpf__open_and_load(); 36 | if (!skel) { 37 | fprintf(stderr, "Failed to open BPF skeleton\n"); 38 | return 1; 39 | } 40 | 41 | /* Attach tracepoint handler */ 42 | err = kprobe_bpf__attach(skel); 43 | if (err) { 44 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 45 | goto cleanup; 46 | } 47 | 48 | if (signal(SIGINT, sig_int) == SIG_ERR) { 49 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | while (!stop) { 57 | fprintf(stderr, "."); 58 | sleep(1); 59 | } 60 | 61 | cleanup: 62 | kprobe_bpf__destroy(skel); 63 | return -err; 64 | } 65 | -------------------------------------------------------------------------------- /src/lsm_connect.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "lsm_connect.skel.h" 13 | 14 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 15 | { 16 | return vfprintf(stderr, format, args); 17 | } 18 | 19 | static volatile sig_atomic_t stop; 20 | 21 | void sig_int(int signo) 22 | { 23 | stop = 1; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | struct lsm_connect_bpf *skel; 29 | int err; 30 | 31 | /* Set up libbpf errors and debug info callback */ 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | /* Open load and verify BPF application */ 35 | skel = lsm_connect_bpf__open_and_load(); 36 | if (!skel) { 37 | fprintf(stderr, "Failed to open BPF skeleton\n"); 38 | return 1; 39 | } 40 | 41 | /* Attach tracepoint handler */ 42 | err = lsm_connect_bpf__attach(skel); 43 | if (err) { 44 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 45 | goto cleanup; 46 | } 47 | 48 | if (signal(SIGINT, sig_int) == SIG_ERR) { 49 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | while (!stop) { 57 | fprintf(stderr, "."); 58 | sleep(1); 59 | } 60 | 61 | cleanup: 62 | lsm_connect_bpf__destroy(skel); 63 | return -err; 64 | } 65 | -------------------------------------------------------------------------------- /src/kprobe_multi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "kprobe_multi.skel.h" 13 | 14 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 15 | { 16 | return vfprintf(stderr, format, args); 17 | } 18 | 19 | static volatile sig_atomic_t stop; 20 | 21 | void sig_int(int signo) 22 | { 23 | stop = 1; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | struct kprobe_multi_bpf *skel; 29 | int err; 30 | 31 | /* Set up libbpf errors and debug info callback */ 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | /* Open load and verify BPF application */ 35 | skel = kprobe_multi_bpf__open_and_load(); 36 | if (!skel) { 37 | fprintf(stderr, "Failed to open BPF skeleton\n"); 38 | return 1; 39 | } 40 | 41 | /* Attach tracepoint handler */ 42 | err = kprobe_multi_bpf__attach(skel); 43 | if (err) { 44 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 45 | goto cleanup; 46 | } 47 | 48 | if (signal(SIGINT, sig_int) == SIG_ERR) { 49 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | while (!stop) { 57 | fprintf(stderr, "."); 58 | sleep(1); 59 | } 60 | 61 | cleanup: 62 | kprobe_multi_bpf__destroy(skel); 63 | return -err; 64 | } 65 | -------------------------------------------------------------------------------- /src/xdp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include "xdp.skel.h" 6 | 7 | #define LO_IFINDEX 3 8 | 9 | static volatile sig_atomic_t exiting = 0; 10 | 11 | static void sig_int(int signo) 12 | { 13 | exiting = 1; 14 | } 15 | 16 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 17 | { 18 | return vfprintf(stderr, format, args); 19 | } 20 | 21 | int main(int argc, char **argv) 22 | { 23 | DECLARE_LIBBPF_OPTS(bpf_xdp_attach_opts, xdp_opts); 24 | struct xdp_bpf *skel; 25 | int prog_fd; 26 | int err; 27 | 28 | libbpf_set_print(libbpf_print_fn); 29 | 30 | skel = xdp_bpf__open_and_load(); 31 | if (!skel) { 32 | fprintf(stderr, "Failed to open BPF skeleton\n"); 33 | return 1; 34 | } 35 | 36 | prog_fd = bpf_program__fd(skel->progs.xdp_pass); 37 | err = bpf_xdp_attach(LO_IFINDEX, prog_fd, 0, &xdp_opts); 38 | if (err) { 39 | fprintf(stderr, "Failed to attach xdp: %d\n", err); 40 | goto cleanup; 41 | } 42 | 43 | if (signal(SIGINT, sig_int) == SIG_ERR) { 44 | err = errno; 45 | fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno)); 46 | goto cleanup; 47 | } 48 | 49 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 50 | "to see output of the BPF program.\n"); 51 | 52 | while (!exiting) { 53 | fprintf(stderr, "."); 54 | sleep(1); 55 | } 56 | 57 | err = bpf_xdp_detach(LO_IFINDEX, 0, &xdp_opts); 58 | if (err) { 59 | fprintf(stderr, "Failed to detach xdp: %d\n", err); 60 | goto cleanup; 61 | } 62 | 63 | cleanup: 64 | xdp_bpf__destroy(skel); 65 | return -err; 66 | } 67 | -------------------------------------------------------------------------------- /src/flow_dissector_load.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 | #ifndef FLOW_DISSECTOR_LOAD 3 | #define FLOW_DISSECTOR_LOAD 4 | 5 | #include 6 | #include 7 | #include "testing_helpers.h" 8 | 9 | static inline int bpf_flow_load(struct bpf_object **obj, 10 | const char *path, 11 | const char *prog_name, 12 | const char *map_name, 13 | const char *keys_map_name, 14 | int *prog_fd, 15 | int *keys_fd) 16 | { 17 | struct bpf_program *prog, *main_prog; 18 | struct bpf_map *prog_array, *keys; 19 | int prog_array_fd; 20 | int ret, fd, i; 21 | 22 | ret = bpf_prog_test_load(path, BPF_PROG_TYPE_FLOW_DISSECTOR, obj, 23 | prog_fd); 24 | if (ret) 25 | return ret; 26 | 27 | main_prog = bpf_object__find_program_by_name(*obj, prog_name); 28 | if (!main_prog) 29 | return -1; 30 | 31 | *prog_fd = bpf_program__fd(main_prog); 32 | if (*prog_fd < 0) 33 | return -1; 34 | 35 | prog_array = bpf_object__find_map_by_name(*obj, map_name); 36 | if (!prog_array) 37 | return -1; 38 | 39 | prog_array_fd = bpf_map__fd(prog_array); 40 | if (prog_array_fd < 0) 41 | return -1; 42 | 43 | if (keys_map_name && keys_fd) { 44 | keys = bpf_object__find_map_by_name(*obj, keys_map_name); 45 | if (!keys) 46 | return -1; 47 | 48 | *keys_fd = bpf_map__fd(keys); 49 | if (*keys_fd < 0) 50 | return -1; 51 | } 52 | 53 | i = 0; 54 | bpf_object__for_each_program(prog, *obj) { 55 | fd = bpf_program__fd(prog); 56 | if (fd < 0) 57 | return fd; 58 | 59 | if (fd != *prog_fd) { 60 | bpf_map_update_elem(prog_array_fd, &i, &fd, BPF_ANY); 61 | ++i; 62 | } 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | #endif /* FLOW_DISSECTOR_LOAD */ 69 | -------------------------------------------------------------------------------- /src/sockfilter.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "sockfilter.h" 11 | 12 | #define IP_MF 0x2000 13 | #define IP_OFFSET 0x1FFF 14 | 15 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_RINGBUF); 19 | __uint(max_entries, 256 * 1024); 20 | } rb SEC(".maps"); 21 | 22 | static inline int ip_is_fragment(struct __sk_buff *skb, __u32 nhoff) 23 | { 24 | __u16 frag_off; 25 | 26 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, frag_off), &frag_off, 2); 27 | frag_off = __bpf_ntohs(frag_off); 28 | return frag_off & (IP_MF | IP_OFFSET); 29 | } 30 | 31 | SEC("socket") 32 | int socket_handler(struct __sk_buff *skb) 33 | { 34 | struct so_event *e; 35 | __u8 verlen; 36 | __u16 proto; 37 | __u32 nhoff = ETH_HLEN; 38 | 39 | bpf_skb_load_bytes(skb, 12, &proto, 2); 40 | proto = __bpf_ntohs(proto); 41 | if (proto != ETH_P_IP) 42 | return 0; 43 | 44 | if (ip_is_fragment(skb, nhoff)) 45 | return 0; 46 | 47 | /* reserve sample from BPF ringbuf */ 48 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 49 | if (!e) 50 | return 0; 51 | 52 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, protocol), &e->ip_proto, 1); 53 | 54 | if (e->ip_proto != IPPROTO_GRE) { 55 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, saddr), &(e->src_addr), 4); 56 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, daddr), &(e->dst_addr), 4); 57 | } 58 | 59 | bpf_skb_load_bytes(skb, nhoff + 0, &verlen, 1); 60 | bpf_skb_load_bytes(skb, nhoff + ((verlen & 0xF) << 2), &(e->ports), 4); 61 | e->pkt_type = skb->pkt_type; 62 | e->ifindex = skb->ifindex; 63 | bpf_ringbuf_submit(e, 0); 64 | 65 | return skb->len; 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bpf-inside: 探索BPF内核的实现 2 | 3 | 通过一些小工具探索BPF在Linux内核的实现。 4 | 5 | ## 目录 6 | 7 | * [01-使用libbpf库编写BPF程序](doc/01-write%20a%20bpf%20program%20with%20libbpf.md) 8 | * [02-Linux性能计数器在内核的实现](doc/02-Performance%20Counters%20for%20Linux.md) 9 | * [03-Tracepoint的内核实现](doc/03-tracepoint%20inside.md) 10 | * [04-CPU-PMU的内核实现](doc/04-cpu%20pmu.md) 11 | * [05-SOFTWARE-PMU的内核实现](doc/05-software%20pmu.md) 12 | * [06-KPROBE的内核实现](doc/06-kprobe%20pmu.md) 13 | * [07-UPROBE的内核实现](doc/07-uprobe.md) 14 | * [08-RAW TRACEPOINT的内核实现](doc/08-raw%20tracepoint.md) 15 | * [09-fentry的内核实现](doc/09-fentry.md) 16 | * [10-KPROBE.MULTI的内核实现](doc/10-kprobe_multi.md) 17 | * [11-BPF LSM的内核实现](doc/11-bpf%20lsm.md) 18 | * [12-XDP的内核实现](doc/12-xdp.md) 19 | * [13-SOCKFILTER的内核实现](doc/13-sockfilter.md) 20 | * [14-TC的内核实现](doc/14-tc.md) 21 | * [15-LWT的内核实现](doc/15-lwt.md) 22 | * [16-IPTABLES_BPF内核实现](doc/16-iptables_bpf.md) 23 | * [17-SK_LOOKUP的内核实现](doc/17-sk_lookup.md) 24 | * [18-CGROUP_BPF的内核实现](doc/18-cgroup.md) 25 | * [19-STRUCT_OPS的内核实现](doc/19-struct_ops.md) 26 | * [20-FLOW_DISSECTOR的内核实现](doc/20-flow_dissector.md) 27 | * [21-SOCKMAP的内核实现](doc/21-sockmap.md) 28 | * [22-LIRC_MODE2的内核实现](doc/22-lirc_mode2.md) 29 | * [23-BPF_ITER的内核实现](doc/23-bpf_iter.md) 30 | * [24-BREAKPOINT-PMU的内核实现](doc/24-hw-breakpoint.md) 31 | * [25-NETFILTER_LINK的内核实现](doc/25-netfilter_link.md) 32 | * [26-UPROBE.MULTI的内核实现](doc/26-uprobe_multi.md) 33 | * [27-TC EXPRESS的内核实现](doc/27-tc_express.md) 34 | * [28-NETKIT的内核实现](doc/28-netkit.md) 35 | * [29-TEST RUN的内核实现](doc/29-test_run.md) 36 | 37 | ## 编译示例 38 | 39 | Makefile build: 40 | 41 | ```shell 42 | $ git submodule update --init --recursive # check out libbpf 43 | $ cd src 44 | $ make 45 | $ sudo ./bin/minimal 46 | <...> 47 | ``` 48 | 49 | CMake build: 50 | 51 | ```shell 52 | $ git submodule update --init --recursive # check out libbpf 53 | $ mkdir build && cd build 54 | $ cmake ../src 55 | $ make 56 | $ sudo ./minimal 57 | <...> 58 | ``` 59 | -------------------------------------------------------------------------------- /src/softirqs.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include "softirqs.h" 7 | #include "bits.bpf.h" 8 | #include "maps.bpf.h" 9 | 10 | const volatile bool targ_dist = false; 11 | const volatile bool targ_ns = false; 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 15 | __uint(max_entries, 1); 16 | __type(key, u32); 17 | __type(value, u64); 18 | } start SEC(".maps"); 19 | 20 | __u64 counts[NR_SOFTIRQS] = {}; 21 | __u64 time[NR_SOFTIRQS] = {}; 22 | struct hist hists[NR_SOFTIRQS] = {}; 23 | 24 | static int handle_entry(unsigned int vec_nr) 25 | { 26 | u64 ts = bpf_ktime_get_ns(); 27 | u32 key = 0; 28 | 29 | bpf_map_update_elem(&start, &key, &ts, BPF_ANY); 30 | return 0; 31 | } 32 | 33 | static int handle_exit(unsigned int vec_nr) 34 | { 35 | u64 delta, *tsp; 36 | u32 key = 0; 37 | 38 | if (vec_nr >= NR_SOFTIRQS) 39 | return 0; 40 | tsp = bpf_map_lookup_elem(&start, &key); 41 | if (!tsp) 42 | return 0; 43 | delta = bpf_ktime_get_ns() - *tsp; 44 | if (!targ_ns) 45 | delta /= 1000U; 46 | 47 | if (!targ_dist) { 48 | __sync_fetch_and_add(&counts[vec_nr], 1); 49 | __sync_fetch_and_add(&time[vec_nr], delta); 50 | } else { 51 | struct hist *hist; 52 | u64 slot; 53 | 54 | hist = &hists[vec_nr]; 55 | slot = log2(delta); 56 | if (slot >= MAX_SLOTS) 57 | slot = MAX_SLOTS - 1; 58 | __sync_fetch_and_add(&hist->slots[slot], 1); 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | SEC("tp_btf/softirq_entry") 65 | int BPF_PROG(softirq_entry_btf, unsigned int vec_nr) 66 | { 67 | return handle_entry(vec_nr); 68 | } 69 | 70 | SEC("tp_btf/softirq_exit") 71 | int BPF_PROG(softirq_exit_btf, unsigned int vec_nr) 72 | { 73 | return handle_exit(vec_nr); 74 | } 75 | 76 | SEC("raw_tp/softirq_entry") 77 | int BPF_PROG(softirq_entry, unsigned int vec_nr) 78 | { 79 | return handle_entry(vec_nr); 80 | } 81 | 82 | SEC("raw_tp/softirq_exit") 83 | int BPF_PROG(softirq_exit, unsigned int vec_nr) 84 | { 85 | return handle_exit(vec_nr); 86 | } 87 | 88 | char LICENSE[] SEC("license") = "GPL"; 89 | -------------------------------------------------------------------------------- /src/runqlen.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "runqlen.h" 8 | 9 | /** 10 | * commit 736c55a02c47 ("sched/fair: Rename cfs_rq.nr_running into nr_queued") 11 | * renamed cfs_rq::nr_running to cfs_rq::nr_queued. 12 | * 13 | * References: 14 | * [0]: https://github.com/torvalds/linux/commit/736c55a02c47 15 | */ 16 | struct cfs_rq___pre_v614 { 17 | unsigned int nr_running; 18 | }; 19 | 20 | static __always_inline __u8 cfs_rq_get_nr_running_or_nr_queued(void *cfs_rq) 21 | { 22 | if (bpf_core_field_exists(struct cfs_rq___pre_v614, nr_running)) 23 | return BPF_CORE_READ((struct cfs_rq___pre_v614 *)cfs_rq, nr_running); 24 | 25 | return BPF_CORE_READ((struct cfs_rq *)cfs_rq, nr_queued); 26 | } 27 | 28 | const volatile bool targ_per_cpu = false; 29 | const volatile bool targ_host = false; 30 | 31 | struct hist hists[MAX_CPU_NR] = {}; 32 | 33 | SEC("perf_event") 34 | int do_sample(struct bpf_perf_event_data *ctx) 35 | { 36 | struct task_struct *task; 37 | struct hist *hist; 38 | u64 slot, cpu = 0; 39 | 40 | task = (void*)bpf_get_current_task(); 41 | if (targ_host) 42 | slot = BPF_CORE_READ(task, se.cfs_rq, rq, nr_running); 43 | else 44 | slot = cfs_rq_get_nr_running_or_nr_queued(BPF_CORE_READ(task, se.cfs_rq)); 45 | /* 46 | * Calculate run queue length by subtracting the currently running task, 47 | * if present. len 0 == idle, len 1 == one running task. 48 | */ 49 | if (slot > 0) 50 | slot--; 51 | if (targ_per_cpu) { 52 | cpu = bpf_get_smp_processor_id(); 53 | /* 54 | * When the program is started, the user space will immediately 55 | * exit when it detects this situation, here just to pass the 56 | * verifier's check. 57 | */ 58 | if (cpu >= MAX_CPU_NR) 59 | return 0; 60 | } 61 | hist = &hists[cpu]; 62 | if (slot >= MAX_SLOTS) 63 | slot = MAX_SLOTS - 1; 64 | if (targ_per_cpu) 65 | hist->slots[slot]++; 66 | else 67 | __sync_fetch_and_add(&hist->slots[slot], 1); 68 | return 0; 69 | } 70 | 71 | char LICENSE[] SEC("license") = "GPL"; 72 | -------------------------------------------------------------------------------- /src/usdt.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "usdt.skel.h" 8 | 9 | static volatile sig_atomic_t exiting; 10 | static jmp_buf env; 11 | 12 | static void sig_int(int signo) 13 | { 14 | exiting = 1; 15 | } 16 | 17 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 18 | { 19 | return vfprintf(stderr, format, args); 20 | } 21 | 22 | static void usdt_trigger() 23 | { 24 | setjmp(env); 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | struct usdt_bpf *skel; 30 | int err; 31 | 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | skel = usdt_bpf__open(); 35 | if (!skel) { 36 | fprintf(stderr, "Failed to open BPF skeleton\n"); 37 | return 1; 38 | } 39 | 40 | skel->bss->my_pid = getpid(); 41 | 42 | err = usdt_bpf__load(skel); 43 | if (!skel) { 44 | fprintf(stderr, "Failed to load BPF skeleton\n"); 45 | return 1; 46 | } 47 | 48 | /* 49 | * Manually attach to libc.so we find. 50 | * We specify pid here, so we don't have to do pid filtering in BPF program. 51 | */ 52 | skel->links.usdt_manual_attach = bpf_program__attach_usdt( 53 | skel->progs.usdt_manual_attach, getpid(), "libc.so.6", "libc", "setjmp", NULL); 54 | if (!skel->links.usdt_manual_attach) { 55 | err = errno; 56 | fprintf(stderr, "Failed to attach BPF program `usdt_manual_attach`\n"); 57 | goto cleanup; 58 | } 59 | 60 | /* 61 | * Auto attach by libbpf, libbpf should be able to find libc.so in your system. 62 | * By default, auto attach does NOT specify pid, so we do pid filtering in BPF program 63 | */ 64 | err = usdt_bpf__attach(skel); 65 | if (err) { 66 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 67 | goto cleanup; 68 | } 69 | 70 | if (signal(SIGINT, sig_int) == SIG_ERR) { 71 | err = errno; 72 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 73 | goto cleanup; 74 | } 75 | 76 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 77 | "to see output of the BPF programs.\n"); 78 | 79 | while (!exiting) { 80 | /* trigger our BPF programs */ 81 | usdt_trigger(); 82 | fprintf(stderr, "."); 83 | sleep(1); 84 | } 85 | 86 | cleanup: 87 | usdt_bpf__destroy(skel); 88 | return -err; 89 | } 90 | -------------------------------------------------------------------------------- /src/bpf_iter.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* Copyright (c) 2020 Facebook */ 3 | // #include "bpf_iter.h" 4 | #include "vmlinux.h" 5 | #include 6 | 7 | char _license[] SEC("license") = "GPL"; 8 | 9 | #define MAX_STACK_TRACE_DEPTH 64 10 | unsigned long entries[MAX_STACK_TRACE_DEPTH] = {}; 11 | #define SIZE_OF_ULONG (sizeof(unsigned long)) 12 | 13 | SEC("iter/task") 14 | int dump_task_stack(struct bpf_iter__task *ctx) 15 | { 16 | struct seq_file *seq = ctx->meta->seq; 17 | struct task_struct *task = ctx->task; 18 | long i, retlen; 19 | 20 | if (task == (void *)0) 21 | return 0; 22 | 23 | retlen = bpf_get_task_stack(task, entries, 24 | MAX_STACK_TRACE_DEPTH * SIZE_OF_ULONG, 0); 25 | if (retlen < 0) 26 | return 0; 27 | 28 | BPF_SEQ_PRINTF(seq, "pid: %8u num_entries: %8u\n", task->pid, 29 | retlen / SIZE_OF_ULONG); 30 | for (i = 0; i < MAX_STACK_TRACE_DEPTH; i++) { 31 | if (retlen > i * SIZE_OF_ULONG) 32 | BPF_SEQ_PRINTF(seq, "[<0>] %pB\n", (void *)entries[i]); 33 | } 34 | BPF_SEQ_PRINTF(seq, "\n"); 35 | 36 | return 0; 37 | } 38 | 39 | SEC("iter/task") 40 | int get_task_user_stacks(struct bpf_iter__task *ctx) 41 | { 42 | struct seq_file *seq = ctx->meta->seq; 43 | struct task_struct *task = ctx->task; 44 | uint64_t buf_sz = 0; 45 | int64_t res; 46 | 47 | if (task == (void *)0) 48 | return 0; 49 | 50 | res = bpf_get_task_stack(task, entries, 51 | MAX_STACK_TRACE_DEPTH * SIZE_OF_ULONG, BPF_F_USER_STACK); 52 | if (res <= 0) 53 | return 0; 54 | 55 | buf_sz += res; 56 | 57 | /* If the verifier doesn't refine bpf_get_task_stack res, and instead 58 | * assumes res is entirely unknown, this program will fail to load as 59 | * the verifier will believe that max buf_sz value allows reading 60 | * past the end of entries in bpf_seq_write call 61 | */ 62 | bpf_seq_write(seq, &entries, buf_sz); 63 | return 0; 64 | } 65 | 66 | SEC("iter/bpf_map") 67 | int dump_bpf_map(struct bpf_iter__bpf_map *ctx) 68 | { 69 | struct seq_file *seq = ctx->meta->seq; 70 | __u64 seq_num = ctx->meta->seq_num; 71 | struct bpf_map *map = ctx->map; 72 | 73 | if (map == (void *)0) { 74 | BPF_SEQ_PRINTF(seq, " %%%%%% END %%%%%%\n"); 75 | return 0; 76 | } 77 | 78 | if (seq_num == 0) 79 | BPF_SEQ_PRINTF(seq, " id refcnt usercnt locked_vm\n"); 80 | 81 | BPF_SEQ_PRINTF(seq, "%8u %8ld %8ld %10lu\n", map->id, map->refcnt.counter, 82 | map->usercnt.counter, 83 | 0LLU); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /src/tc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include "tc.skel.h" 7 | 8 | #define LO_IFINDEX 3 9 | 10 | static volatile sig_atomic_t exiting = 0; 11 | 12 | static void sig_int(int signo) 13 | { 14 | exiting = 1; 15 | } 16 | 17 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 18 | { 19 | return vfprintf(stderr, format, args); 20 | } 21 | 22 | int main(int argc, char **argv) 23 | { 24 | DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = if_nametoindex("lo"), 25 | .attach_point = BPF_TC_INGRESS); 26 | DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1); 27 | bool hook_created = false; 28 | struct tc_bpf *skel; 29 | int err; 30 | 31 | libbpf_set_print(libbpf_print_fn); 32 | 33 | skel = tc_bpf__open_and_load(); 34 | if (!skel) { 35 | fprintf(stderr, "Failed to open BPF skeleton\n"); 36 | return 1; 37 | } 38 | 39 | /* The hook (i.e. qdisc) may already exists because: 40 | * 1. it is created by other processes or users 41 | * 2. or since we are attaching to the TC ingress ONLY, 42 | * bpf_tc_hook_destroy does NOT really remove the qdisc, 43 | * there may be an egress filter on the qdisc 44 | */ 45 | err = bpf_tc_hook_create(&tc_hook); 46 | if (!err) 47 | hook_created = true; 48 | if (err && err != -EEXIST) { 49 | fprintf(stderr, "Failed to create TC hook: %d\n", err); 50 | goto cleanup; 51 | } 52 | 53 | tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_ingress); 54 | err = bpf_tc_attach(&tc_hook, &tc_opts); 55 | if (err) { 56 | fprintf(stderr, "Failed to attach TC: %d\n", err); 57 | goto cleanup; 58 | } 59 | 60 | if (signal(SIGINT, sig_int) == SIG_ERR) { 61 | err = errno; 62 | fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno)); 63 | goto cleanup; 64 | } 65 | 66 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 67 | "to see output of the BPF program.\n"); 68 | 69 | while (!exiting) { 70 | fprintf(stderr, "."); 71 | sleep(1); 72 | } 73 | 74 | tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; 75 | err = bpf_tc_detach(&tc_hook, &tc_opts); 76 | if (err) { 77 | fprintf(stderr, "Failed to detach TC: %d\n", err); 78 | goto cleanup; 79 | } 80 | 81 | cleanup: 82 | if (hook_created) 83 | bpf_tc_hook_destroy(&tc_hook); 84 | tc_bpf__destroy(skel); 85 | return -err; 86 | } 87 | -------------------------------------------------------------------------------- /src/bpf_iter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "bpf_iter.skel.h" 14 | 15 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 16 | { 17 | return vfprintf(stderr, format, args); 18 | } 19 | 20 | static void do_dummy_read_opts(struct bpf_program *prog, struct bpf_iter_attach_opts *opts) 21 | { 22 | char buf[256] = {}; 23 | int iter_fd, len; 24 | struct bpf_link *link; 25 | 26 | link = bpf_program__attach_iter(prog, opts); 27 | if (link == NULL) 28 | { 29 | fprintf(stderr, "failed to attach_iter,%m\n"); 30 | return; 31 | } 32 | iter_fd = bpf_iter_create(bpf_link__fd(link)); 33 | if (iter_fd < 0) 34 | { 35 | fprintf(stderr, "failed to create_iter,%m\n"); 36 | goto free_link; 37 | } 38 | /* not check contents, but ensure read() ends without error */ 39 | while ((len = read(iter_fd, buf, sizeof(buf) - 1)) > 0) 40 | { 41 | buf[len] = 0; 42 | fprintf(stderr, "%s", buf); 43 | } 44 | printf("\n"); 45 | 46 | close(iter_fd); 47 | 48 | free_link: 49 | bpf_link__destroy(link); 50 | } 51 | 52 | static void do_dummy_read(struct bpf_program *prog) 53 | { 54 | do_dummy_read_opts(prog, NULL); 55 | } 56 | 57 | int main(int argc, char **argv) 58 | { 59 | struct bpf_iter_bpf *skel; 60 | LIBBPF_OPTS(bpf_iter_attach_opts, opts); 61 | union bpf_iter_link_info linfo; 62 | int err; 63 | 64 | /* Set up libbpf errors and debug info callback */ 65 | libbpf_set_print(libbpf_print_fn); 66 | 67 | /* Open load and verify BPF application */ 68 | skel = bpf_iter_bpf__open_and_load(); 69 | if (!skel) 70 | { 71 | fprintf(stderr, "Failed to open BPF skeleton\n"); 72 | return 1; 73 | } 74 | printf("PID %d\n", getpid()); 75 | 76 | memset(&linfo, 0, sizeof(linfo)); 77 | linfo.task.tid = getpid(); 78 | opts.link_info = &linfo; 79 | opts.link_info_len = sizeof(linfo); 80 | do_dummy_read_opts(skel->progs.dump_task_stack, &opts); 81 | // do_dummy_read(skel->progs.dump_task_stack); 82 | // do_dummy_read(skel->links.get_task_user_stacks); 83 | do_dummy_read(skel->progs.dump_bpf_map); 84 | 85 | cleanup: 86 | bpf_iter_bpf__destroy(skel); 87 | return -err; 88 | } 89 | -------------------------------------------------------------------------------- /src/helpers/map_helpers.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #include 4 | #include 5 | #include 6 | 7 | #include "map_helpers.h" 8 | 9 | #define warn(...) fprintf(stderr, __VA_ARGS__) 10 | 11 | static bool batch_map_ops = true; /* hope for the best */ 12 | 13 | static int 14 | dump_hash_iter(int map_fd, void *keys, __u32 key_size, 15 | void *values, __u32 value_size, __u32 *count, 16 | void *invalid_key) 17 | { 18 | __u8 key[key_size], next_key[key_size]; 19 | __u32 n = 0; 20 | int i, err; 21 | 22 | /* First get keys */ 23 | __builtin_memcpy(key, invalid_key, key_size); 24 | while (n < *count) { 25 | err = bpf_map_get_next_key(map_fd, key, next_key); 26 | if (err && errno != ENOENT) { 27 | return -1; 28 | } else if (err) { 29 | break; 30 | } 31 | __builtin_memcpy(key, next_key, key_size); 32 | __builtin_memcpy(keys + key_size * n, next_key, key_size); 33 | n++; 34 | } 35 | 36 | /* Now read values */ 37 | for (i = 0; i < n; i++) { 38 | err = bpf_map_lookup_elem(map_fd, keys + key_size * i, 39 | values + value_size * i); 40 | if (err) 41 | return -1; 42 | } 43 | 44 | *count = n; 45 | return 0; 46 | } 47 | 48 | static int 49 | dump_hash_batch(int map_fd, void *keys, __u32 key_size, 50 | void *values, __u32 value_size, __u32 *count) 51 | { 52 | void *in = NULL, *out; 53 | __u32 n, n_read = 0; 54 | int err = 0; 55 | 56 | while (n_read < *count && !err) { 57 | n = *count - n_read; 58 | err = bpf_map_lookup_batch(map_fd, &in, &out, 59 | keys + n_read * key_size, 60 | values + n_read * value_size, 61 | &n, NULL); 62 | if (err && errno != ENOENT) { 63 | return -1; 64 | } 65 | n_read += n; 66 | in = out; 67 | } 68 | 69 | *count = n_read; 70 | return 0; 71 | } 72 | 73 | int dump_hash(int map_fd, 74 | void *keys, __u32 key_size, 75 | void *values, __u32 value_size, 76 | __u32 *count, void *invalid_key) 77 | { 78 | int err; 79 | 80 | if (!keys || !values || !count || !key_size || !value_size) { 81 | errno = EINVAL; 82 | return -1; 83 | } 84 | 85 | if (batch_map_ops) { 86 | err = dump_hash_batch(map_fd, keys, key_size, 87 | values, value_size, count); 88 | if (err) { 89 | if (errno != EINVAL) { 90 | return -1; 91 | 92 | /* assume that batch operations are not 93 | * supported and try non-batch mode */ 94 | batch_map_ops = false; 95 | } 96 | } 97 | } 98 | 99 | if (!invalid_key) { 100 | errno = EINVAL; 101 | return -1; 102 | } 103 | 104 | return dump_hash_iter(map_fd, keys, key_size, 105 | values, value_size, count, invalid_key); 106 | } 107 | -------------------------------------------------------------------------------- /src/flow_dissector_load.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "flow_dissector_load.h" 15 | 16 | const char *cfg_pin_path = "/sys/fs/bpf/flow_dissector"; 17 | const char *cfg_map_name = "jmp_table"; 18 | bool cfg_attach = true; 19 | char *cfg_prog_name; 20 | char *cfg_path_name; 21 | 22 | static void load_and_attach_program(void) 23 | { 24 | int prog_fd, ret; 25 | struct bpf_object *obj; 26 | 27 | /* Use libbpf 1.0 API mode */ 28 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 29 | 30 | ret = bpf_flow_load(&obj, cfg_path_name, cfg_prog_name, 31 | cfg_map_name, NULL, &prog_fd, NULL); 32 | if (ret) 33 | error(1, 0, "bpf_flow_load %s", cfg_path_name); 34 | 35 | ret = bpf_prog_attach(prog_fd, 0 /* Ignore */, BPF_FLOW_DISSECTOR, 0); 36 | if (ret) 37 | error(1, 0, "bpf_prog_attach %s", cfg_path_name); 38 | 39 | ret = bpf_object__pin(obj, cfg_pin_path); 40 | if (ret) 41 | error(1, 0, "bpf_object__pin %s", cfg_pin_path); 42 | } 43 | 44 | static void detach_program(void) 45 | { 46 | char command[64]; 47 | int ret; 48 | 49 | ret = bpf_prog_detach(0, BPF_FLOW_DISSECTOR); 50 | if (ret) 51 | error(1, 0, "bpf_prog_detach"); 52 | 53 | /* To unpin, it is necessary and sufficient to just remove this dir */ 54 | sprintf(command, "rm -r %s", cfg_pin_path); 55 | ret = system(command); 56 | if (ret) 57 | error(1, errno, "%s", command); 58 | } 59 | 60 | static void parse_opts(int argc, char **argv) 61 | { 62 | bool attach = false; 63 | bool detach = false; 64 | int c; 65 | 66 | while ((c = getopt(argc, argv, "adp:s:")) != -1) { 67 | switch (c) { 68 | case 'a': 69 | if (detach) 70 | error(1, 0, "attach/detach are exclusive"); 71 | attach = true; 72 | break; 73 | case 'd': 74 | if (attach) 75 | error(1, 0, "attach/detach are exclusive"); 76 | detach = true; 77 | break; 78 | case 'p': 79 | if (cfg_path_name) 80 | error(1, 0, "only one path can be given"); 81 | 82 | cfg_path_name = optarg; 83 | break; 84 | case 's': 85 | if (cfg_prog_name) 86 | error(1, 0, "only one prog can be given"); 87 | 88 | cfg_prog_name = optarg; 89 | break; 90 | } 91 | } 92 | 93 | if (detach) 94 | cfg_attach = false; 95 | 96 | if (cfg_attach && !cfg_path_name) 97 | error(1, 0, "must provide a path to the BPF program"); 98 | 99 | if (cfg_attach && !cfg_prog_name) 100 | error(1, 0, "must provide a section name"); 101 | } 102 | 103 | int main(int argc, char **argv) 104 | { 105 | parse_opts(argc, argv); 106 | if (cfg_attach) 107 | load_and_attach_program(); 108 | else 109 | detach_program(); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/helpers/compat.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | 4 | #include "compat.h" 5 | #include "trace_helpers.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #define PERF_BUFFER_PAGES 64 11 | 12 | struct bpf_buffer { 13 | struct bpf_map *events; 14 | void *inner; 15 | bpf_buffer_sample_fn fn; 16 | void *ctx; 17 | int type; 18 | }; 19 | 20 | static void perfbuf_sample_fn(void *ctx, int cpu, void *data, __u32 size) 21 | { 22 | struct bpf_buffer *buffer = ctx; 23 | bpf_buffer_sample_fn fn; 24 | 25 | fn = buffer->fn; 26 | if (!fn) 27 | return; 28 | 29 | (void)fn(buffer->ctx, data, size); 30 | } 31 | 32 | struct bpf_buffer *bpf_buffer__new(struct bpf_map *events, struct bpf_map *heap) 33 | { 34 | struct bpf_buffer *buffer; 35 | bool use_ringbuf; 36 | int type; 37 | 38 | use_ringbuf = probe_ringbuf(); 39 | if (use_ringbuf) { 40 | bpf_map__set_autocreate(heap, false); 41 | type = BPF_MAP_TYPE_RINGBUF; 42 | } else { 43 | bpf_map__set_type(events, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 44 | bpf_map__set_key_size(events, sizeof(int)); 45 | bpf_map__set_value_size(events, sizeof(int)); 46 | type = BPF_MAP_TYPE_PERF_EVENT_ARRAY; 47 | } 48 | 49 | buffer = calloc(1, sizeof(*buffer)); 50 | if (!buffer) { 51 | errno = ENOMEM; 52 | return NULL; 53 | } 54 | 55 | buffer->events = events; 56 | buffer->type = type; 57 | return buffer; 58 | } 59 | 60 | int bpf_buffer__open(struct bpf_buffer *buffer, bpf_buffer_sample_fn sample_cb, 61 | bpf_buffer_lost_fn lost_cb, void *ctx) 62 | { 63 | int fd, type; 64 | void *inner; 65 | 66 | fd = bpf_map__fd(buffer->events); 67 | type = buffer->type; 68 | 69 | switch (type) { 70 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: 71 | buffer->fn = sample_cb; 72 | buffer->ctx = ctx; 73 | inner = perf_buffer__new(fd, PERF_BUFFER_PAGES, perfbuf_sample_fn, lost_cb, buffer, NULL); 74 | break; 75 | case BPF_MAP_TYPE_RINGBUF: 76 | inner = ring_buffer__new(fd, sample_cb, ctx, NULL); 77 | break; 78 | default: 79 | return 0; 80 | } 81 | 82 | if (!inner) 83 | return -errno; 84 | 85 | buffer->inner = inner; 86 | return 0; 87 | } 88 | 89 | int bpf_buffer__poll(struct bpf_buffer *buffer, int timeout_ms) 90 | { 91 | switch (buffer->type) { 92 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: 93 | return perf_buffer__poll(buffer->inner, timeout_ms); 94 | case BPF_MAP_TYPE_RINGBUF: 95 | return ring_buffer__poll(buffer->inner, timeout_ms); 96 | default: 97 | return -EINVAL; 98 | } 99 | } 100 | 101 | void bpf_buffer__free(struct bpf_buffer *buffer) 102 | { 103 | if (!buffer) 104 | return; 105 | 106 | switch (buffer->type) { 107 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: 108 | perf_buffer__free(buffer->inner); 109 | break; 110 | case BPF_MAP_TYPE_RINGBUF: 111 | ring_buffer__free(buffer->inner); 112 | break; 113 | } 114 | free(buffer); 115 | } 116 | -------------------------------------------------------------------------------- /src/uprobe_multi.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | char _license[] SEC("license") = "GPL"; 8 | 9 | __u64 uprobe_multi_func_1_addr = 0; 10 | __u64 uprobe_multi_func_2_addr = 0; 11 | __u64 uprobe_multi_func_3_addr = 0; 12 | 13 | __u64 uprobe_multi_func_1_result = 0; 14 | __u64 uprobe_multi_func_2_result = 0; 15 | __u64 uprobe_multi_func_3_result = 0; 16 | 17 | __u64 uretprobe_multi_func_1_result = 0; 18 | __u64 uretprobe_multi_func_2_result = 0; 19 | __u64 uretprobe_multi_func_3_result = 0; 20 | 21 | __u64 uprobe_multi_sleep_result = 0; 22 | 23 | int pid = 0; 24 | int child_pid = 0; 25 | 26 | bool test_cookie = false; 27 | void *user_ptr = 0; 28 | 29 | static __always_inline bool verify_sleepable_user_copy(void) 30 | { 31 | char data[9]; 32 | 33 | bpf_copy_from_user(data, sizeof(data), user_ptr); 34 | return bpf_strncmp(data, sizeof(data), "test_data") == 0; 35 | } 36 | 37 | static void uprobe_multi_check(void *ctx, bool is_return, bool is_sleep) 38 | { 39 | child_pid = bpf_get_current_pid_tgid() >> 32; 40 | 41 | if (pid && child_pid != pid) 42 | return; 43 | 44 | __u64 cookie = test_cookie ? bpf_get_attach_cookie(ctx) : 0; 45 | __u64 addr = bpf_get_func_ip(ctx); 46 | 47 | #define SET(__var, __addr, __cookie) ({ \ 48 | if (addr == __addr && \ 49 | (!test_cookie || (cookie == __cookie))) \ 50 | __var += 1; \ 51 | }) 52 | 53 | if (is_return) { 54 | SET(uretprobe_multi_func_1_result, uprobe_multi_func_1_addr, 2); 55 | SET(uretprobe_multi_func_2_result, uprobe_multi_func_2_addr, 3); 56 | SET(uretprobe_multi_func_3_result, uprobe_multi_func_3_addr, 1); 57 | } else { 58 | SET(uprobe_multi_func_1_result, uprobe_multi_func_1_addr, 3); 59 | SET(uprobe_multi_func_2_result, uprobe_multi_func_2_addr, 1); 60 | SET(uprobe_multi_func_3_result, uprobe_multi_func_3_addr, 2); 61 | } 62 | 63 | #undef SET 64 | 65 | if (is_sleep && verify_sleepable_user_copy()) 66 | uprobe_multi_sleep_result += 1; 67 | } 68 | 69 | SEC("uprobe.multi//proc/self/exe:uprobe_multi_func_*") 70 | int uprobe(struct pt_regs *ctx) 71 | { 72 | uprobe_multi_check(ctx, false, false); 73 | return 0; 74 | } 75 | 76 | SEC("uretprobe.multi//proc/self/exe:uprobe_multi_func_*") 77 | int uretprobe(struct pt_regs *ctx) 78 | { 79 | uprobe_multi_check(ctx, true, false); 80 | return 0; 81 | } 82 | 83 | SEC("uprobe.multi.s//proc/self/exe:uprobe_multi_func_*") 84 | int uprobe_sleep(struct pt_regs *ctx) 85 | { 86 | uprobe_multi_check(ctx, false, true); 87 | return 0; 88 | } 89 | 90 | SEC("uretprobe.multi.s//proc/self/exe:uprobe_multi_func_*") 91 | int uretprobe_sleep(struct pt_regs *ctx) 92 | { 93 | uprobe_multi_check(ctx, true, true); 94 | return 0; 95 | } 96 | 97 | SEC("uprobe.multi//proc/self/exe:uprobe_multi_func_*") 98 | int uprobe_extra(struct pt_regs *ctx) 99 | { 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /src/test_tc_link.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* Copyright (c) 2023 Isovalent */ 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | char LICENSE[] SEC("license") = "GPL"; 14 | 15 | bool seen_tc1; 16 | bool seen_tc2; 17 | bool seen_tc3; 18 | bool seen_tc4; 19 | bool seen_tc5; 20 | bool seen_tc6; 21 | bool seen_tc7; 22 | bool seen_tc8; 23 | 24 | bool set_type; 25 | 26 | bool seen_eth; 27 | bool seen_host; 28 | bool seen_mcast; 29 | 30 | int mark, prio; 31 | unsigned short headroom, tailroom; 32 | 33 | SEC("tc/ingress") 34 | int tc1(struct __sk_buff *skb) 35 | { 36 | struct ethhdr eth = {}; 37 | 38 | if (skb->protocol != __bpf_constant_htons(ETH_P_IP)) 39 | goto out; 40 | if (bpf_skb_load_bytes(skb, 0, ð, sizeof(eth))) 41 | goto out; 42 | seen_eth = eth.h_proto == bpf_htons(ETH_P_IP); 43 | seen_host = skb->pkt_type == PACKET_HOST; 44 | if (seen_host && set_type) { 45 | eth.h_dest[0] = 4; 46 | if (bpf_skb_store_bytes(skb, 0, ð, sizeof(eth), 0)) 47 | goto fail; 48 | bpf_skb_change_type(skb, PACKET_MULTICAST); 49 | } 50 | out: 51 | seen_tc1 = true; 52 | fail: 53 | return TCX_NEXT; 54 | } 55 | 56 | SEC("tc/egress") 57 | int tc2(struct __sk_buff *skb) 58 | { 59 | seen_tc2 = true; 60 | return TCX_NEXT; 61 | } 62 | 63 | SEC("tc/egress") 64 | int tc3(struct __sk_buff *skb) 65 | { 66 | seen_tc3 = true; 67 | return TCX_NEXT; 68 | } 69 | 70 | SEC("tc/egress") 71 | int tc4(struct __sk_buff *skb) 72 | { 73 | seen_tc4 = true; 74 | return TCX_NEXT; 75 | } 76 | 77 | SEC("tc/egress") 78 | int tc5(struct __sk_buff *skb) 79 | { 80 | seen_tc5 = true; 81 | return TCX_PASS; 82 | } 83 | 84 | SEC("tc/egress") 85 | int tc6(struct __sk_buff *skb) 86 | { 87 | seen_tc6 = true; 88 | return TCX_PASS; 89 | } 90 | 91 | SEC("tc/ingress") 92 | int tc7(struct __sk_buff *skb) 93 | { 94 | struct ethhdr eth = {}; 95 | 96 | if (skb->protocol != __bpf_constant_htons(ETH_P_IP)) 97 | goto out; 98 | if (bpf_skb_load_bytes(skb, 0, ð, sizeof(eth))) 99 | goto out; 100 | if (eth.h_dest[0] == 4 && set_type) { 101 | seen_mcast = skb->pkt_type == PACKET_MULTICAST; 102 | bpf_skb_change_type(skb, PACKET_HOST); 103 | } 104 | out: 105 | seen_tc7 = true; 106 | return TCX_PASS; 107 | } 108 | 109 | struct sk_buff { 110 | struct net_device *dev; 111 | }; 112 | 113 | struct net_device { 114 | unsigned short needed_headroom; 115 | unsigned short needed_tailroom; 116 | }; 117 | 118 | SEC("tc/egress") 119 | int tc8(struct __sk_buff *skb) 120 | { 121 | struct net_device *dev = BPF_CORE_READ((struct sk_buff *)skb, dev); 122 | 123 | seen_tc8 = true; 124 | mark = skb->mark; 125 | prio = skb->priority; 126 | headroom = BPF_CORE_READ(dev, needed_headroom); 127 | tailroom = BPF_CORE_READ(dev, needed_tailroom); 128 | return TCX_PASS; 129 | } 130 | -------------------------------------------------------------------------------- /src/trace_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TRACE_HELPERS_H 3 | #define __TRACE_HELPERS_H 4 | 5 | #include 6 | 7 | #define NSEC_PER_SEC 1000000000ULL 8 | 9 | struct ksym { 10 | const char *name; 11 | unsigned long addr; 12 | }; 13 | 14 | struct ksyms; 15 | 16 | struct ksyms *ksyms__load(void); 17 | void ksyms__free(struct ksyms *ksyms); 18 | const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, 19 | unsigned long addr); 20 | const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, 21 | const char *name); 22 | 23 | struct sym { 24 | const char *name; 25 | unsigned long start; 26 | unsigned long size; 27 | unsigned long offset; 28 | }; 29 | 30 | struct syms; 31 | 32 | struct syms *syms__load_pid(int tgid); 33 | struct syms *syms__load_file(const char *fname); 34 | void syms__free(struct syms *syms); 35 | const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); 36 | const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, 37 | char **dso_name, unsigned long *dso_offset); 38 | 39 | struct syms_cache; 40 | 41 | struct syms_cache *syms_cache__new(int nr); 42 | struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); 43 | void syms_cache__free(struct syms_cache *syms_cache); 44 | 45 | struct partition { 46 | char *name; 47 | unsigned int dev; 48 | }; 49 | 50 | struct partitions; 51 | 52 | struct partitions *partitions__load(void); 53 | void partitions__free(struct partitions *partitions); 54 | const struct partition * 55 | partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); 56 | const struct partition * 57 | partitions__get_by_name(const struct partitions *partitions, const char *name); 58 | 59 | void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); 60 | void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, 61 | unsigned int step, const char *val_type); 62 | 63 | unsigned long long get_ktime_ns(void); 64 | 65 | bool is_kernel_module(const char *name); 66 | 67 | /* 68 | * When attempting to use kprobe/kretprobe, please check out new fentry/fexit 69 | * probes, as they provide better performance and usability. But in some 70 | * situations we have to fallback to kprobe/kretprobe probes. This helper 71 | * is used to detect fentry/fexit support for the specified kernel function. 72 | * 73 | * 1. A gap between kernel versions, kernel BTF is exposed 74 | * starting from 5.4 kernel. but fentry/fexit is actually 75 | * supported starting from 5.5. 76 | * 2. Whether kernel supports module BTF or not 77 | * 78 | * *name* is the name of a kernel function to be attached to, which can be 79 | * from vmlinux or a kernel module. 80 | * *mod* is a hint that indicates the *name* may reside in module BTF, 81 | * if NULL, it means *name* belongs to vmlinux. 82 | */ 83 | bool fentry_can_attach(const char *name, const char *mod); 84 | 85 | /* 86 | * The name of a kernel function to be attached to may be changed between 87 | * kernel releases. This helper is used to confirm whether the target kernel 88 | * uses a certain function name before attaching. 89 | * 90 | * It is achieved by scaning 91 | * /sys/kernel/debug/tracing/available_filter_functions 92 | * If this file does not exist, it fallbacks to parse /proc/kallsyms, 93 | * which is slower. 94 | */ 95 | bool kprobe_exists(const char *name); 96 | bool tracepoint_exists(const char *category, const char *event); 97 | 98 | bool vmlinux_btf_exists(void); 99 | bool module_btf_exists(const char *mod); 100 | 101 | bool probe_tp_btf(const char *name); 102 | bool probe_ringbuf(); 103 | 104 | #endif /* __TRACE_HELPERS_H */ 105 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | cmake_minimum_required(VERSION 3.16) 4 | project(examples) 5 | 6 | # Tell cmake where to find BpfObject module 7 | get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE) 8 | list(APPEND CMAKE_MODULE_PATH ${PROJECT_ROOT}/tools/cmake) 9 | 10 | # Build vendored libbpf 11 | include(ExternalProject) 12 | ExternalProject_Add(libbpf 13 | PREFIX libbpf 14 | SOURCE_DIR ${PROJECT_ROOT}/libbpf/src 15 | CONFIGURE_COMMAND "" 16 | BUILD_COMMAND make 17 | BUILD_STATIC_ONLY=1 18 | OBJDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf 19 | DESTDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf 20 | INCLUDEDIR= 21 | LIBDIR= 22 | UAPIDIR= 23 | install install_uapi_headers 24 | BUILD_IN_SOURCE TRUE 25 | INSTALL_COMMAND "" 26 | STEP_TARGETS build 27 | ) 28 | 29 | ExternalProject_Add(bpftool 30 | PREFIX bpftool 31 | SOURCE_DIR ${PROJECT_ROOT}/bpftool/src 32 | CONFIGURE_COMMAND "" 33 | BUILD_COMMAND make bootstrap 34 | OUTPUT=${CMAKE_CURRENT_BINARY_DIR}/bpftool/ 35 | BUILD_IN_SOURCE TRUE 36 | INSTALL_COMMAND "" 37 | STEP_TARGETS build 38 | ) 39 | 40 | find_program(CARGO_EXISTS cargo) 41 | if(CARGO_EXISTS) 42 | ExternalProject_Add(blazesym 43 | PREFIX blazesym 44 | SOURCE_DIR ${PROJECT_ROOT}/blazesym 45 | CONFIGURE_COMMAND "" 46 | BUILD_COMMAND cargo build --package=blazesym-c --release 47 | BUILD_IN_SOURCE TRUE 48 | INSTALL_COMMAND "" 49 | STEP_TARGETS build 50 | ) 51 | endif() 52 | 53 | # Set BpfObject input parameters -- note this is usually not necessary unless 54 | # you're in a highly vendored environment (like libbpf-bootstrap) 55 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") 56 | set(ARCH "x86") 57 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") 58 | set(ARCH "arm") 59 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") 60 | set(ARCH "arm64") 61 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ppc64le") 62 | set(ARCH "powerpc") 63 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "mips") 64 | set(ARCH "mips") 65 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "riscv64") 66 | set(ARCH "riscv") 67 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "loongarch64") 68 | set(ARCH "loongarch") 69 | endif() 70 | 71 | set(BPFOBJECT_BPFTOOL_EXE ${CMAKE_CURRENT_BINARY_DIR}/bpftool/bootstrap/bpftool) 72 | set(BPFOBJECT_VMLINUX_H ${PROJECT_ROOT}/vmlinux.h/include/${ARCH}/vmlinux.h) 73 | set(LIBBPF_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/libbpf) 74 | set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf.a) 75 | find_package(BpfObject REQUIRED) 76 | 77 | file(GLOB helpers *_helpers.c) 78 | # list(APPEND helpers compat.c) 79 | add_library(helper STATIC ${helpers}) 80 | target_include_directories(helper PRIVATE ${LIBBPF_INCLUDE_DIRS}) 81 | 82 | set(BZ_APPS profile data_breakpoint) 83 | 84 | # Create an executable for each application 85 | file(GLOB apps *.bpf.c) 86 | if(NOT CARGO_EXISTS) 87 | foreach(app ${BZ_APPS}) 88 | list(REMOVE_ITEM apps ${CMAKE_CURRENT_SOURCE_DIR}/${app}.bpf.c) 89 | endforeach() 90 | endif() 91 | foreach(app ${apps}) 92 | get_filename_component(app_stem ${app} NAME_WE) 93 | 94 | # Build object skeleton and depend skeleton on libbpf build 95 | bpf_object(${app_stem} ${app_stem}.bpf.c) 96 | add_dependencies(${app_stem}_skel libbpf-build bpftool-build) 97 | 98 | add_executable(${app_stem} ${app_stem}.c) 99 | target_link_libraries(${app_stem} ${app_stem}_skel helper) 100 | endforeach() 101 | 102 | if(CARGO_EXISTS) 103 | foreach(app_stem ${BZ_APPS}) 104 | target_include_directories(${app_stem} PRIVATE 105 | ${PROJECT_ROOT}/blazesym/include) 106 | target_link_libraries(${app_stem} 107 | ${PROJECT_ROOT}/blazesym/target/release/libblazesym_c.a -lpthread -lrt -ldl) 108 | endforeach() 109 | endif() 110 | -------------------------------------------------------------------------------- /src/uprobe.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "uprobe.skel.h" 9 | 10 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 11 | { 12 | return vfprintf(stderr, format, args); 13 | } 14 | 15 | /* 16 | * Taken from https://github.com/torvalds/linux/blob/9b59ec8d50a1f28747ceff9a4f39af5deba9540e/tools/testing/selftests/bpf/trace_helpers.c#L149-L205 17 | * 18 | * See discussion in https://github.com/libbpf/libbpf-bootstrap/pull/90 19 | */ 20 | ssize_t get_uprobe_offset(const void *addr) 21 | { 22 | size_t start, end, base; 23 | char buf[256]; 24 | bool found = false; 25 | FILE *f; 26 | 27 | f = fopen("/proc/self/maps", "r"); 28 | if (!f) 29 | return -errno; 30 | 31 | while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) 32 | { 33 | if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) 34 | { 35 | found = true; 36 | break; 37 | } 38 | } 39 | 40 | fclose(f); 41 | 42 | if (!found) 43 | return -ESRCH; 44 | 45 | return (uintptr_t)addr - start + base; 46 | } 47 | 48 | /* It's a global function to make sure compiler doesn't inline it. */ 49 | int uprobed_add(int a, int b) 50 | { 51 | return a + b; 52 | } 53 | 54 | int uprobed_sub(int a, int b) 55 | { 56 | return a - b; 57 | } 58 | 59 | int main(int argc, char **argv) 60 | { 61 | struct uprobe_bpf *skel; 62 | long uprobe_offset; 63 | int err, i; 64 | 65 | /* Set up libbpf errors and debug info callback */ 66 | libbpf_set_print(libbpf_print_fn); 67 | 68 | /* Load and verify BPF application */ 69 | skel = uprobe_bpf__open_and_load(); 70 | if (!skel) 71 | { 72 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 73 | return 1; 74 | } 75 | 76 | /* uprobe/uretprobe expects relative offset of the function to attach 77 | * to. This offset is relateve to the process's base load address. So 78 | * easy way to do this is to take an absolute address of the desired 79 | * function and substract base load address from it. If we were to 80 | * parse ELF to calculate this function, we'd need to add .text 81 | * section offset and function's offset within .text ELF section. 82 | */ 83 | uprobe_offset = get_uprobe_offset(&uprobed_add); 84 | 85 | /* Attach tracepoint handler */ 86 | skel->links.uprobe_add = 87 | bpf_program__attach_uprobe(skel->progs.uprobe_add, false /* not uretprobe */, 88 | 0 /* self pid */, "/proc/self/exe", uprobe_offset); 89 | 90 | if (!skel->links.uprobe_add) 91 | { 92 | err = -errno; 93 | fprintf(stderr, "Failed to attach uprobe: %d\n", err); 94 | goto cleanup; 95 | } 96 | 97 | /* we can also attach uprobe/uretprobe to any existing or future 98 | * processes that use the same binary executable; to do that we need 99 | * to specify -1 as PID, as we do here 100 | */ 101 | skel->links.uretprobe_add = 102 | bpf_program__attach_uprobe(skel->progs.uretprobe_add, true /* uretprobe */, 103 | -1 /* any pid */, "/proc/self/exe", uprobe_offset); 104 | 105 | if (!skel->links.uretprobe_add) 106 | { 107 | err = -errno; 108 | fprintf(stderr, "Failed to attach uprobe: %d\n", err); 109 | goto cleanup; 110 | } 111 | 112 | /* Let libbpf perform auto-attach for uprobe_sub/uretprobe_sub 113 | * NOTICE: we provide path and symbol info in SEC for BPF programs 114 | */ 115 | err = uprobe_bpf__attach(skel); 116 | if (err) 117 | { 118 | fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err); 119 | goto cleanup; 120 | } 121 | 122 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 123 | "to see output of the BPF programs.\n"); 124 | 125 | for (i = 0;; i++) 126 | { 127 | /* trigger our BPF programs */ 128 | fprintf(stderr, "."); 129 | uprobed_add(i, i + 1); 130 | uprobed_sub(i * i, i); 131 | sleep(1); 132 | } 133 | 134 | cleanup: 135 | uprobe_bpf__destroy(skel); 136 | return -err; 137 | } 138 | -------------------------------------------------------------------------------- /src/netfilter_link_attach.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "netfilter_link_attach.skel.h" 8 | 9 | #define _CHECK(condition, tag, duration, format...) ({ \ 10 | int __ret = !!(condition); \ 11 | int __save_errno = errno; \ 12 | if (__ret) { \ 13 | fprintf(stdout, "%s:FAIL:%s ", __func__, tag); \ 14 | fprintf(stdout, ##format); \ 15 | } else { \ 16 | fprintf(stdout, "%s:PASS:%s %d nsec\n", \ 17 | __func__, tag, duration); \ 18 | } \ 19 | errno = __save_errno; \ 20 | __ret; \ 21 | }) 22 | 23 | #define CHECK(condition, tag, format...) \ 24 | _CHECK(condition, tag, duration, format) 25 | 26 | #define ASSERT_OK_PTR(ptr, name) ({ \ 27 | static int duration = 0; \ 28 | const void *___res = (ptr); \ 29 | int ___err = libbpf_get_error(___res); \ 30 | bool ___ok = ___err == 0; \ 31 | CHECK(!___ok, (name), "unexpected error: %d\n", ___err); \ 32 | ___ok; \ 33 | }) 34 | 35 | #define ASSERT_ERR_PTR(ptr, name) ({ \ 36 | static int duration = 0; \ 37 | const void *___res = (ptr); \ 38 | int ___err = libbpf_get_error(___res); \ 39 | bool ___ok = ___err != 0; \ 40 | CHECK(!___ok, (name), "unexpected pointer: %p\n", ___res); \ 41 | ___ok; \ 42 | }) 43 | 44 | #define ASSERT_OK(res, name) ({ \ 45 | static int duration = 0; \ 46 | long long ___res = (res); \ 47 | bool ___ok = ___res == 0; \ 48 | CHECK(!___ok, (name), "unexpected error: %lld (errno %d)\n", \ 49 | ___res, errno); \ 50 | ___ok; \ 51 | }) 52 | 53 | #ifndef ARRAY_SIZE 54 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 55 | #endif 56 | 57 | struct nf_link_test { 58 | __u32 pf; 59 | __u32 hooknum; 60 | __s32 priority; 61 | __u32 flags; 62 | 63 | bool expect_success; 64 | const char * const name; 65 | }; 66 | 67 | static const struct nf_link_test nf_hook_link_tests[] = { 68 | { .name = "allzero", }, 69 | { .pf = NFPROTO_NUMPROTO, .name = "invalid-pf", }, 70 | { .pf = NFPROTO_IPV4, .hooknum = 42, .name = "invalid-hooknum", }, 71 | { .pf = NFPROTO_IPV4, .priority = INT_MIN, .name = "invalid-priority-min", }, 72 | { .pf = NFPROTO_IPV4, .priority = INT_MAX, .name = "invalid-priority-max", }, 73 | { .pf = NFPROTO_IPV4, .flags = UINT_MAX, .name = "invalid-flags", }, 74 | 75 | { .pf = NFPROTO_INET, .priority = 1, .name = "invalid-inet-not-supported", }, 76 | 77 | { .pf = NFPROTO_IPV4, .priority = -10000, .expect_success = true, .name = "attach ipv4", }, 78 | { .pf = NFPROTO_IPV6, .priority = 10001, .expect_success = true, .name = "attach ipv6", }, 79 | }; 80 | 81 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 82 | { 83 | return vfprintf(stderr, format, args); 84 | } 85 | 86 | int main(int argc, char *const argv[]) 87 | { 88 | struct netfilter_link_attach_bpf *skel; 89 | struct bpf_program *prog; 90 | LIBBPF_OPTS(bpf_netfilter_opts, opts); 91 | int i; 92 | libbpf_set_print(libbpf_print_fn); 93 | 94 | skel = netfilter_link_attach_bpf__open_and_load(); 95 | if (!ASSERT_OK_PTR(skel, "netfilter_link_attach_bpf_open_and_load")) 96 | goto out; 97 | 98 | prog = skel->progs.nf_link_attach_test; 99 | if (!ASSERT_OK_PTR(prog, "attach program")) 100 | goto out; 101 | 102 | for (i = 0; i < ARRAY_SIZE(nf_hook_link_tests); i++) { 103 | struct bpf_link *link; 104 | 105 | #define X(opts, m, i) opts.m = nf_hook_link_tests[(i)].m 106 | X(opts, pf, i); 107 | X(opts, hooknum, i); 108 | X(opts, priority, i); 109 | X(opts, flags, i); 110 | #undef X 111 | link = bpf_program__attach_netfilter(prog, &opts); 112 | if (nf_hook_link_tests[i].expect_success) { 113 | struct bpf_link *link2; 114 | 115 | if (!ASSERT_OK_PTR(link, "program attach successful")) 116 | continue; 117 | 118 | link2 = bpf_program__attach_netfilter(prog, &opts); 119 | ASSERT_ERR_PTR(link2, "attach program with same pf/hook/priority"); 120 | 121 | if (!ASSERT_OK(bpf_link__destroy(link), "link destroy")) 122 | break; 123 | 124 | link2 = bpf_program__attach_netfilter(prog, &opts); 125 | if (!ASSERT_OK_PTR(link2, "program reattach successful")) 126 | continue; 127 | if (!ASSERT_OK(bpf_link__destroy(link2), "link destroy")) 128 | break; 129 | } else { 130 | ASSERT_ERR_PTR(link, "program load failure"); 131 | } 132 | } 133 | 134 | out: 135 | netfilter_link_attach_bpf__destroy(skel); 136 | return 0; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | OUTPUT := .output 3 | APPS_OUTPUT ?= bin 4 | CLANG ?= clang 5 | LIBBPF_SRC := $(abspath ../libbpf/src) 6 | BPFTOOL_SRC := $(abspath ../bpftool/src) 7 | LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) 8 | BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) 9 | BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool 10 | LIBBLAZESYM_SRC := $(abspath ../blazesym/) 11 | LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/include) 12 | LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a) 13 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ 14 | | sed 's/arm.*/arm/' \ 15 | | sed 's/aarch64/arm64/' \ 16 | | sed 's/ppc64le/powerpc/' \ 17 | | sed 's/mips.*/mips/' \ 18 | | sed 's/riscv64/riscv/' \ 19 | | sed 's/loongarch64/loongarch/') 20 | VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h 21 | # Use our own libbpf API headers and Linux UAPI headers distributed with 22 | # libbpf to avoid dependency on system-wide headers, which could be missing or 23 | # outdated 24 | INCLUDES := -I$(OUTPUT) -I../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) 25 | CFLAGS := -g -Wall 26 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 27 | 28 | APPS = minimal 29 | 30 | CARGO ?= $(shell which cargo) 31 | ifeq ($(strip $(CARGO)),) 32 | BZS_APPS := 33 | else 34 | BZS_APPS := 35 | APPS += $(BZS_APPS) 36 | # Required by libblazesym 37 | ALL_LDFLAGS += -lrt -ldl -lpthread -lm 38 | endif 39 | 40 | # Get Clang's default includes on this system. We'll explicitly add these dirs 41 | # to the includes list when compiling with `-target bpf` because otherwise some 42 | # architecture-specific dirs will be "missing" on some architectures/distros - 43 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 44 | # sys/cdefs.h etc. might be missing. 45 | # 46 | # Use '-idirafter': Don't interfere with include mechanics except where the 47 | # build would have failed anyways. 48 | CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - &1 \ 49 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 50 | 51 | ifeq ($(V),1) 52 | Q = 53 | msg = 54 | else 55 | Q = @ 56 | msg = @printf ' %-8s %s%s\n' \ 57 | "$(1)" \ 58 | "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ 59 | "$(if $(3), $(3))"; 60 | MAKEFLAGS += --no-print-directory 61 | endif 62 | 63 | define allow-override 64 | $(if $(or $(findstring environment,$(origin $(1))),\ 65 | $(findstring command line,$(origin $(1)))),,\ 66 | $(eval $(1) = $(2))) 67 | endef 68 | 69 | $(call allow-override,CC,$(CROSS_COMPILE)cc) 70 | $(call allow-override,LD,$(CROSS_COMPILE)ld) 71 | 72 | .PHONY: all 73 | all: $(APPS) 74 | 75 | .PHONY: clean 76 | clean: 77 | $(call msg,CLEAN) 78 | $(Q)rm -rf $(OUTPUT) $(APPS) 79 | 80 | $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT) $(APPS_OUTPUT): 81 | $(call msg,MKDIR,$@) 82 | $(Q)mkdir -p $@ 83 | 84 | # Build libbpf 85 | $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf 86 | $(call msg,LIB,$@) 87 | $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ 88 | OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ 89 | INCLUDEDIR= LIBDIR= UAPIDIR= \ 90 | install 91 | 92 | # Build bpftool 93 | $(BPFTOOL): | $(BPFTOOL_OUTPUT) 94 | $(call msg,BPFTOOL,$@) 95 | $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap 96 | 97 | 98 | $(LIBBLAZESYM_SRC)/target/release/libblazesym.a:: 99 | $(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --release 100 | 101 | $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) 102 | $(call msg,LIB, $@) 103 | $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ 104 | 105 | # Build BPF code 106 | $(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) 107 | $(call msg,BPF,$@) 108 | $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ 109 | $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ 110 | -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) 111 | $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) 112 | 113 | # Generate BPF skeletons 114 | $(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) 115 | $(call msg,GEN-SKEL,$@) 116 | $(Q)$(BPFTOOL) gen skeleton $< > $@ 117 | 118 | # Build user-space code 119 | $(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h 120 | 121 | $(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) 122 | $(call msg,CC,$@) 123 | $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ 124 | 125 | $(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_OBJ) 126 | 127 | $(BZS_APPS): $(LIBBLAZESYM_OBJ) 128 | 129 | # Build application binary 130 | $(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) ${APPS_OUTPUT} 131 | $(call msg,BINARY,$@) 132 | $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $(APPS_OUTPUT)/$@ 133 | 134 | # delete failed targets 135 | .DELETE_ON_ERROR: 136 | 137 | # keep intermediate (.skel.h, .bpf.o, etc) targets 138 | .SECONDARY: 139 | -------------------------------------------------------------------------------- /src/test_lirc_mode2.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // test ir decoder 3 | // 4 | // Copyright (C) 2018 Sean Young 5 | 6 | // A lirc chardev is a device representing a consumer IR (cir) device which 7 | // can receive infrared signals from remote control and/or transmit IR. 8 | // 9 | // IR is sent as a series of pulses and space somewhat like morse code. The 10 | // BPF program can decode this into scancodes so that rc-core can translate 11 | // this into input key codes using the rc keymap. 12 | // 13 | // This test works by sending IR over rc-loopback, so the IR is processed by 14 | // BPF and then decoded into scancodes. The lirc chardev must be the one 15 | // associated with rc-loopback, see the output of ir-keytable(1). 16 | // 17 | // The following CONFIG options must be enabled for the test to succeed: 18 | // CONFIG_RC_CORE=y 19 | // CONFIG_BPF_RAWIR_EVENT=y 20 | // CONFIG_RC_LOOPBACK=y 21 | 22 | // Steps: 23 | // 1. Open the /dev/lircN device for rc-loopback (given on command line) 24 | // 2. Attach bpf_lirc_mode2 program which decodes some IR. 25 | // 3. Send some IR to the same IR device; since it is loopback, this will 26 | // end up in the bpf program 27 | // 4. bpf program should decode IR and report keycode 28 | // 5. We can read keycode from same /dev/lirc device 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include "test_lirc_mode2.skel.h" 46 | 47 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 48 | { 49 | return vfprintf(stderr, format, args); 50 | } 51 | 52 | int main(int argc, char **argv) 53 | { 54 | struct test_lirc_mode2_bpf *skel; 55 | int ret, lircfd, progfd, inputfd; 56 | int testir1 = 0x1dead; 57 | int testir2 = 0x20101; 58 | uint32_t prog_ids[10], prog_flags[10], prog_cnt; 59 | 60 | if (argc != 3) { 61 | printf("Usage: %s /dev/lircN /dev/input/eventM\n", argv[0]); 62 | return 2; 63 | } 64 | 65 | libbpf_set_print(libbpf_print_fn); 66 | skel = test_lirc_mode2_bpf__open_and_load(); 67 | if (!skel) { 68 | fprintf(stderr, "Failed to open BPF skeleton\n"); 69 | return 1; 70 | } 71 | progfd = bpf_program__fd(skel->progs.bpf_decoder); 72 | 73 | lircfd = open(argv[1], O_RDWR | O_NONBLOCK); 74 | if (lircfd == -1) { 75 | printf("failed to open lirc device %s: %m\n", argv[1]); 76 | return 1; 77 | } 78 | 79 | /* Let's try detach it before it was ever attached */ 80 | ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2); 81 | if (ret != -1 || errno != ENOENT) { 82 | printf("bpf_prog_detach2 not attached should fail: %m\n"); 83 | return 1; 84 | } 85 | 86 | inputfd = open(argv[2], O_RDONLY | O_NONBLOCK); 87 | if (inputfd == -1) { 88 | printf("failed to open input device %s: %m\n", argv[1]); 89 | return 1; 90 | } 91 | 92 | prog_cnt = 10; 93 | ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids, 94 | &prog_cnt); 95 | if (ret) { 96 | printf("Failed to query bpf programs on lirc device: %m\n"); 97 | return 1; 98 | } 99 | 100 | if (prog_cnt != 0) { 101 | printf("Expected nothing to be attached\n"); 102 | return 1; 103 | } 104 | 105 | ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0); 106 | if (ret) { 107 | printf("Failed to attach bpf to lirc device: %m\n"); 108 | return 1; 109 | } 110 | 111 | /* Write raw IR */ 112 | ret = write(lircfd, &testir1, sizeof(testir1)); 113 | if (ret != sizeof(testir1)) { 114 | printf("Failed to send test IR message: %m\n"); 115 | return 1; 116 | } 117 | 118 | struct pollfd pfd = { .fd = inputfd, .events = POLLIN }; 119 | struct input_event event; 120 | 121 | for (;;) { 122 | poll(&pfd, 1, 100); 123 | 124 | /* Read decoded IR */ 125 | ret = read(inputfd, &event, sizeof(event)); 126 | if (ret != sizeof(event)) { 127 | printf("Failed to read decoded IR: %m\n"); 128 | return 1; 129 | } 130 | 131 | if (event.type == EV_MSC && event.code == MSC_SCAN && 132 | event.value == 0xdead) { 133 | break; 134 | } 135 | } 136 | 137 | /* Write raw IR */ 138 | ret = write(lircfd, &testir2, sizeof(testir2)); 139 | if (ret != sizeof(testir2)) { 140 | printf("Failed to send test IR message: %m\n"); 141 | return 1; 142 | } 143 | 144 | for (;;) { 145 | poll(&pfd, 1, 100); 146 | 147 | /* Read decoded IR */ 148 | ret = read(inputfd, &event, sizeof(event)); 149 | if (ret != sizeof(event)) { 150 | printf("Failed to read decoded IR: %m\n"); 151 | return 1; 152 | } 153 | 154 | if (event.type == EV_REL && event.code == REL_Y && 155 | event.value == 1 ) { 156 | break; 157 | } 158 | } 159 | 160 | prog_cnt = 10; 161 | ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids, 162 | &prog_cnt); 163 | if (ret) { 164 | printf("Failed to query bpf programs on lirc device: %m\n"); 165 | return 1; 166 | } 167 | 168 | if (prog_cnt != 1) { 169 | printf("Expected one program to be attached\n"); 170 | return 1; 171 | } 172 | 173 | /* Let's try detaching it now it is actually attached */ 174 | ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2); 175 | if (ret) { 176 | printf("bpf_prog_detach2: returned %m\n"); 177 | return 1; 178 | } 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /src/uprobe_multi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "uprobe_multi.skel.h" 9 | 10 | #define ASSERT_EQ(actual, expected, name) ({ \ 11 | typeof(actual) ___act = (actual); \ 12 | typeof(expected) ___exp = (expected); \ 13 | bool ___ok = ___act == ___exp; \ 14 | if (!___ok) \ 15 | printf("unexpected %s: actual %lld != expected %lld\n", \ 16 | (name), (long long)(___act), (long long)(___exp)); \ 17 | ___ok; \ 18 | }) 19 | 20 | #define ASSERT_OK_PTR(ptr, name) ({ \ 21 | const void *___res = (ptr); \ 22 | int ___err = libbpf_get_error(___res); \ 23 | bool ___ok = ___err == 0; \ 24 | if (!___ok) \ 25 | printf("%s\n", name); \ 26 | ___ok; \ 27 | }) 28 | 29 | #ifndef ARRAY_SIZE 30 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 31 | #endif 32 | 33 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 34 | { 35 | return vfprintf(stderr, format, args); 36 | } 37 | 38 | struct child 39 | { 40 | int go[2]; 41 | int pid; 42 | }; 43 | 44 | static char test_data[] = "test_data"; 45 | void uprobe_multi_func_1(void) 46 | { 47 | asm volatile(""); 48 | } 49 | 50 | void uprobe_multi_func_2(void) 51 | { 52 | asm volatile(""); 53 | } 54 | 55 | void uprobe_multi_func_3(void) 56 | { 57 | asm volatile(""); 58 | } 59 | 60 | static void uprobe_multi_test_run(struct uprobe_multi_bpf *skel, struct child *child) 61 | { 62 | skel->bss->uprobe_multi_func_1_addr = (__u64)uprobe_multi_func_1; 63 | skel->bss->uprobe_multi_func_2_addr = (__u64)uprobe_multi_func_2; 64 | skel->bss->uprobe_multi_func_3_addr = (__u64)uprobe_multi_func_3; 65 | 66 | skel->bss->user_ptr = test_data; 67 | 68 | /* 69 | * Disable pid check in bpf program if we are pid filter test, 70 | * because the probe should be executed only by child->pid 71 | * passed at the probe attach. 72 | */ 73 | skel->bss->pid = child ? 0 : getpid(); 74 | 75 | /* trigger all probes */ 76 | uprobe_multi_func_1(); 77 | uprobe_multi_func_2(); 78 | uprobe_multi_func_3(); 79 | 80 | /* 81 | * There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123] 82 | * function and each slepable probe (6) increments uprobe_multi_sleep_result. 83 | */ 84 | ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 2, "uprobe_multi_func_1_result"); 85 | ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 2, "uprobe_multi_func_2_result"); 86 | ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 2, "uprobe_multi_func_3_result"); 87 | 88 | ASSERT_EQ(skel->bss->uretprobe_multi_func_1_result, 2, "uretprobe_multi_func_1_result"); 89 | ASSERT_EQ(skel->bss->uretprobe_multi_func_2_result, 2, "uretprobe_multi_func_2_result"); 90 | ASSERT_EQ(skel->bss->uretprobe_multi_func_3_result, 2, "uretprobe_multi_func_3_result"); 91 | 92 | ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result"); 93 | } 94 | 95 | static void 96 | test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts, 97 | struct child *child) 98 | { 99 | pid_t pid = child ? child->pid : -1; 100 | struct uprobe_multi_bpf *skel = NULL; 101 | 102 | skel = uprobe_multi_bpf__open_and_load(); 103 | if (!ASSERT_OK_PTR(skel, "uprobe_multi_bpf__open_and_load")) 104 | goto cleanup; 105 | 106 | opts->retprobe = false; 107 | skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, pid, 108 | binary, pattern, opts); 109 | if (!ASSERT_OK_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) 110 | goto cleanup; 111 | 112 | opts->retprobe = true; 113 | skel->links.uretprobe = bpf_program__attach_uprobe_multi(skel->progs.uretprobe, pid, 114 | binary, pattern, opts); 115 | if (!ASSERT_OK_PTR(skel->links.uretprobe, "bpf_program__attach_uprobe_multi")) 116 | goto cleanup; 117 | 118 | opts->retprobe = false; 119 | skel->links.uprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uprobe_sleep, pid, 120 | binary, pattern, opts); 121 | if (!ASSERT_OK_PTR(skel->links.uprobe_sleep, "bpf_program__attach_uprobe_multi")) 122 | goto cleanup; 123 | 124 | opts->retprobe = true; 125 | skel->links.uretprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uretprobe_sleep, 126 | pid, binary, pattern, opts); 127 | if (!ASSERT_OK_PTR(skel->links.uretprobe_sleep, "bpf_program__attach_uprobe_multi")) 128 | goto cleanup; 129 | 130 | opts->retprobe = false; 131 | skel->links.uprobe_extra = bpf_program__attach_uprobe_multi(skel->progs.uprobe_extra, -1, 132 | binary, pattern, opts); 133 | if (!ASSERT_OK_PTR(skel->links.uprobe_extra, "bpf_program__attach_uprobe_multi")) 134 | goto cleanup; 135 | 136 | uprobe_multi_test_run(skel, child); 137 | 138 | cleanup: 139 | uprobe_multi_bpf__destroy(skel); 140 | } 141 | 142 | int main(int argc, char **argv) 143 | { 144 | libbpf_set_print(libbpf_print_fn); 145 | LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); 146 | const char *syms[3] = { 147 | "uprobe_multi_func_1", 148 | "uprobe_multi_func_2", 149 | "uprobe_multi_func_3", 150 | }; 151 | 152 | opts.syms = syms; 153 | opts.cnt = ARRAY_SIZE(syms); 154 | test_attach_api("/proc/self/exe", NULL, &opts, NULL); 155 | printf("PASSED\n"); 156 | return 0; 157 | } 158 | -------------------------------------------------------------------------------- /src/sockfilter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "sockfilter.h" 17 | #include "sockfilter.skel.h" 18 | 19 | static struct env { 20 | const char *interface; 21 | } env; 22 | 23 | const char argp_program_doc[] = 24 | "BPF socket filter demo application.\n" 25 | "\n" 26 | "This program watch network packet of specified interface and print out src/dst\n" 27 | "information.\n" 28 | "\n" 29 | "Currently only IPv4 is supported.\n" 30 | "\n" 31 | "USAGE: ./sockfilter [-i ]\n"; 32 | 33 | static const struct argp_option opts[] = { 34 | { "interface", 'i', "INTERFACE", 0, "Network interface to attach" }, 35 | {}, 36 | }; 37 | 38 | static error_t parse_arg(int key, char *arg, struct argp_state *state) 39 | { 40 | switch (key) { 41 | case 'i': 42 | env.interface = arg; 43 | break; 44 | case ARGP_KEY_ARG: 45 | argp_usage(state); 46 | break; 47 | default: 48 | return ARGP_ERR_UNKNOWN; 49 | } 50 | return 0; 51 | } 52 | 53 | static const struct argp argp = { 54 | .options = opts, 55 | .parser = parse_arg, 56 | .doc = argp_program_doc, 57 | }; 58 | 59 | static const char *ipproto_mapping[IPPROTO_MAX] = { 60 | [IPPROTO_IP] = "IP", [IPPROTO_ICMP] = "ICMP", [IPPROTO_IGMP] = "IGMP", 61 | [IPPROTO_IPIP] = "IPIP", [IPPROTO_TCP] = "TCP", [IPPROTO_EGP] = "EGP", 62 | [IPPROTO_PUP] = "PUP", [IPPROTO_UDP] = "UDP", [IPPROTO_IDP] = "IDP", 63 | [IPPROTO_TP] = "TP", [IPPROTO_DCCP] = "DCCP", [IPPROTO_IPV6] = "IPV6", 64 | [IPPROTO_RSVP] = "RSVP", [IPPROTO_GRE] = "GRE", [IPPROTO_ESP] = "ESP", 65 | [IPPROTO_AH] = "AH", [IPPROTO_MTP] = "MTP", [IPPROTO_BEETPH] = "BEETPH", 66 | [IPPROTO_ENCAP] = "ENCAP", [IPPROTO_PIM] = "PIM", [IPPROTO_COMP] = "COMP", 67 | [IPPROTO_SCTP] = "SCTP", [IPPROTO_UDPLITE] = "UDPLITE", [IPPROTO_MPLS] = "MPLS", 68 | [IPPROTO_RAW] = "RAW" 69 | }; 70 | 71 | static int open_raw_sock(const char *name) 72 | { 73 | struct sockaddr_ll sll; 74 | int sock; 75 | 76 | sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); 77 | if (sock < 0) { 78 | fprintf(stderr, "Failed to create raw socket\n"); 79 | return -1; 80 | } 81 | 82 | memset(&sll, 0, sizeof(sll)); 83 | sll.sll_family = AF_PACKET; 84 | sll.sll_ifindex = if_nametoindex(name); 85 | sll.sll_protocol = htons(ETH_P_ALL); 86 | if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { 87 | fprintf(stderr, "Failed to bind to %s: %s\n", name, strerror(errno)); 88 | close(sock); 89 | return -1; 90 | } 91 | 92 | return sock; 93 | } 94 | 95 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 96 | { 97 | return vfprintf(stderr, format, args); 98 | } 99 | 100 | static inline void ltoa(uint32_t addr, char *dst) 101 | { 102 | snprintf(dst, 16, "%u.%u.%u.%u", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, 103 | (addr >> 8) & 0xFF, (addr & 0xFF)); 104 | } 105 | 106 | static int handle_event(void *ctx, void *data, size_t data_sz) 107 | { 108 | const struct so_event *e = data; 109 | char ifname[IF_NAMESIZE]; 110 | char sstr[16] = {}, dstr[16] = {}; 111 | 112 | if (e->pkt_type != PACKET_HOST) 113 | return 0; 114 | 115 | if (e->ip_proto < 0 || e->ip_proto >= IPPROTO_MAX) 116 | return 0; 117 | 118 | if (!if_indextoname(e->ifindex, ifname)) 119 | return 0; 120 | 121 | ltoa(ntohl(e->src_addr), sstr); 122 | ltoa(ntohl(e->dst_addr), dstr); 123 | 124 | printf("interface: %s\tprotocol: %s\t%s:%d(src) -> %s:%d(dst)\n", ifname, 125 | ipproto_mapping[e->ip_proto], sstr, ntohs(e->port16[0]), dstr, ntohs(e->port16[1])); 126 | 127 | return 0; 128 | } 129 | 130 | static volatile bool exiting = false; 131 | 132 | static void sig_handler(int sig) 133 | { 134 | exiting = true; 135 | } 136 | 137 | int main(int argc, char **argv) 138 | { 139 | struct ring_buffer *rb = NULL; 140 | struct sockfilter_bpf *skel; 141 | int err, prog_fd, sock; 142 | 143 | env.interface = "lo"; 144 | /* Parse command line arguments */ 145 | err = argp_parse(&argp, argc, argv, 0, NULL, NULL); 146 | if (err) 147 | return -err; 148 | 149 | /* Set up libbpf errors and debug info callback */ 150 | libbpf_set_print(libbpf_print_fn); 151 | 152 | /* Cleaner handling of Ctrl-C */ 153 | signal(SIGINT, sig_handler); 154 | signal(SIGTERM, sig_handler); 155 | 156 | /* Load and verify BPF programs*/ 157 | skel = sockfilter_bpf__open_and_load(); 158 | if (!skel) { 159 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 160 | return 1; 161 | } 162 | 163 | /* Set up ring buffer polling */ 164 | rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); 165 | if (!rb) { 166 | err = -1; 167 | fprintf(stderr, "Failed to create ring buffer\n"); 168 | goto cleanup; 169 | } 170 | 171 | /* Create raw socket for localhost interface */ 172 | sock = open_raw_sock(env.interface); 173 | if (sock < 0) { 174 | err = -2; 175 | fprintf(stderr, "Failed to open raw socket\n"); 176 | goto cleanup; 177 | } 178 | 179 | /* Attach BPF program to raw socket */ 180 | prog_fd = bpf_program__fd(skel->progs.socket_handler); 181 | if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) { 182 | err = -3; 183 | fprintf(stderr, "Failed to attach to raw socket\n"); 184 | goto cleanup; 185 | } 186 | 187 | /* Process events */ 188 | while (!exiting) { 189 | err = ring_buffer__poll(rb, 100 /* timeout, ms */); 190 | /* Ctrl-C will cause -EINTR */ 191 | if (err == -EINTR) { 192 | err = 0; 193 | break; 194 | } 195 | if (err < 0) { 196 | fprintf(stderr, "Error polling perf buffer: %d\n", err); 197 | break; 198 | } 199 | sleep(1); 200 | } 201 | 202 | cleanup: 203 | ring_buffer__free(rb); 204 | sockfilter_bpf__destroy(skel); 205 | return -err; 206 | } 207 | -------------------------------------------------------------------------------- /src/btf_helpers.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "trace_helpers.h" 11 | #include "btf_helpers.h" 12 | 13 | extern unsigned char _binary_min_core_btfs_tar_gz_start[] __attribute__((weak)); 14 | extern unsigned char _binary_min_core_btfs_tar_gz_end[] __attribute__((weak)); 15 | 16 | #define FIELD_LEN 65 17 | #define ID_FMT "ID=%64s" 18 | #define VERSION_FMT "VERSION_ID=\"%64s" 19 | 20 | struct os_info { 21 | char id[FIELD_LEN]; 22 | char version[FIELD_LEN]; 23 | char arch[FIELD_LEN]; 24 | char kernel_release[FIELD_LEN]; 25 | }; 26 | 27 | static struct os_info * get_os_info() 28 | { 29 | struct os_info *info = NULL; 30 | struct utsname u; 31 | size_t len = 0; 32 | ssize_t read; 33 | char *line = NULL; 34 | FILE *f; 35 | 36 | if (uname(&u) == -1) 37 | return NULL; 38 | 39 | f = fopen("/etc/os-release", "r"); 40 | if (!f) 41 | return NULL; 42 | 43 | info = calloc(1, sizeof(*info)); 44 | if (!info) 45 | goto out; 46 | 47 | strncpy(info->kernel_release, u.release, FIELD_LEN); 48 | strncpy(info->arch, u.machine, FIELD_LEN); 49 | 50 | while ((read = getline(&line, &len, f)) != -1) { 51 | if (sscanf(line, ID_FMT, info->id) == 1) 52 | continue; 53 | 54 | if (sscanf(line, VERSION_FMT, info->version) == 1) { 55 | /* remove '"' suffix */ 56 | info->version[strlen(info->version) - 1] = 0; 57 | continue; 58 | } 59 | } 60 | 61 | out: 62 | free(line); 63 | fclose(f); 64 | 65 | return info; 66 | } 67 | 68 | #define INITIAL_BUF_SIZE (1024 * 1024 * 4) /* 4MB */ 69 | 70 | /* adapted from https://zlib.net/zlib_how.html */ 71 | static int 72 | inflate_gz(unsigned char *src, int src_size, unsigned char **dst, int *dst_size) 73 | { 74 | size_t size = INITIAL_BUF_SIZE; 75 | size_t next_size = size; 76 | z_stream strm; 77 | void *tmp; 78 | int ret; 79 | 80 | strm.zalloc = Z_NULL; 81 | strm.zfree = Z_NULL; 82 | strm.opaque = Z_NULL; 83 | strm.avail_in = 0; 84 | strm.next_in = Z_NULL; 85 | 86 | ret = inflateInit2(&strm, 16 + MAX_WBITS); 87 | if (ret != Z_OK) 88 | return -EINVAL; 89 | 90 | *dst = malloc(size); 91 | if (!*dst) 92 | return -ENOMEM; 93 | 94 | strm.next_in = src; 95 | strm.avail_in = src_size; 96 | 97 | /* run inflate() on input until it returns Z_STREAM_END */ 98 | do { 99 | strm.next_out = *dst + strm.total_out; 100 | strm.avail_out = next_size; 101 | ret = inflate(&strm, Z_NO_FLUSH); 102 | if (ret != Z_OK && ret != Z_STREAM_END) 103 | goto out_err; 104 | /* we need more space */ 105 | if (strm.avail_out == 0) { 106 | next_size = size; 107 | size *= 2; 108 | tmp = realloc(*dst, size); 109 | if (!tmp) { 110 | ret = -ENOMEM; 111 | goto out_err; 112 | } 113 | *dst = tmp; 114 | } 115 | } while (ret != Z_STREAM_END); 116 | 117 | *dst_size = strm.total_out; 118 | 119 | /* clean up and return */ 120 | ret = inflateEnd(&strm); 121 | if (ret != Z_OK) { 122 | ret = -EINVAL; 123 | goto out_err; 124 | } 125 | return 0; 126 | 127 | out_err: 128 | free(*dst); 129 | *dst = NULL; 130 | return ret; 131 | } 132 | 133 | /* tar header from https://github.com/tklauser/libtar/blob/v1.2.20/lib/libtar.h#L39-L60 */ 134 | struct tar_header { 135 | char name[100]; 136 | char mode[8]; 137 | char uid[8]; 138 | char gid[8]; 139 | char size[12]; 140 | char mtime[12]; 141 | char chksum[8]; 142 | char typeflag; 143 | char linkname[100]; 144 | char magic[6]; 145 | char version[2]; 146 | char uname[32]; 147 | char gname[32]; 148 | char devmajor[8]; 149 | char devminor[8]; 150 | char prefix[155]; 151 | char padding[12]; 152 | }; 153 | 154 | static char *tar_file_start(struct tar_header *tar, const char *name, int *length) 155 | { 156 | while (tar->name[0]) { 157 | sscanf(tar->size, "%o", length); 158 | if (!strcmp(tar->name, name)) 159 | return (char *)(tar + 1); 160 | tar += 1 + (*length + 511)/512; 161 | } 162 | return NULL; 163 | } 164 | 165 | int ensure_core_btf(struct bpf_object_open_opts *opts) 166 | { 167 | char name_fmt[] = "./%s/%s/%s/%s.btf"; 168 | char btf_path[] = "/tmp/bcc-libbpf-tools.btf.XXXXXX"; 169 | struct os_info *info = NULL; 170 | unsigned char *dst_buf = NULL; 171 | char *file_start; 172 | int dst_size = 0; 173 | char name[100]; 174 | FILE *dst = NULL; 175 | int ret; 176 | 177 | /* do nothing if the system provides BTF */ 178 | if (vmlinux_btf_exists()) 179 | return 0; 180 | 181 | /* compiled without min core btfs */ 182 | if (!_binary_min_core_btfs_tar_gz_start) 183 | return -EOPNOTSUPP; 184 | 185 | info = get_os_info(); 186 | if (!info) 187 | return -errno; 188 | 189 | ret = mkstemp(btf_path); 190 | if (ret < 0) { 191 | ret = -errno; 192 | goto out; 193 | } 194 | 195 | dst = fdopen(ret, "wb"); 196 | if (!dst) { 197 | ret = -errno; 198 | goto out; 199 | } 200 | 201 | ret = snprintf(name, sizeof(name), name_fmt, info->id, info->version, 202 | info->arch, info->kernel_release); 203 | if (ret < 0 || ret == sizeof(name)) { 204 | ret = -EINVAL; 205 | goto out; 206 | } 207 | 208 | ret = inflate_gz(_binary_min_core_btfs_tar_gz_start, 209 | _binary_min_core_btfs_tar_gz_end - _binary_min_core_btfs_tar_gz_start, 210 | &dst_buf, &dst_size); 211 | if (ret < 0) 212 | goto out; 213 | 214 | ret = 0; 215 | file_start = tar_file_start((struct tar_header *)dst_buf, name, &dst_size); 216 | if (!file_start) { 217 | ret = -EINVAL; 218 | goto out; 219 | } 220 | 221 | if (fwrite(file_start, 1, dst_size, dst) != dst_size) { 222 | ret = -ferror(dst); 223 | goto out; 224 | } 225 | 226 | opts->btf_custom_path = strdup(btf_path); 227 | if (!opts->btf_custom_path) 228 | ret = -ENOMEM; 229 | 230 | out: 231 | free(info); 232 | fclose(dst); 233 | free(dst_buf); 234 | 235 | return ret; 236 | } 237 | 238 | void cleanup_core_btf(struct bpf_object_open_opts *opts) { 239 | if (!opts) 240 | return; 241 | 242 | if (!opts->btf_custom_path) 243 | return; 244 | 245 | unlink(opts->btf_custom_path); 246 | free((void *)opts->btf_custom_path); 247 | } 248 | -------------------------------------------------------------------------------- /src/test_flow_dissector.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Load BPF flow dissector and verify it correctly dissects traffic 5 | 6 | BPF_FILE="bpf_flow.bpf.o" 7 | export TESTNAME=test_flow_dissector 8 | unmount=0 9 | 10 | # Kselftest framework requirement - SKIP code is 4. 11 | ksft_skip=4 12 | 13 | msg="skip all tests:" 14 | if [ $UID != 0 ]; then 15 | echo $msg please run this as root >&2 16 | exit $ksft_skip 17 | fi 18 | 19 | # This test needs to be run in a network namespace with in_netns.sh. Check if 20 | # this is the case and run it with in_netns.sh if it is being run in the root 21 | # namespace. 22 | if [[ -z $(ip netns identify $$) ]]; then 23 | err=0 24 | if bpftool="$(which bpftool)"; then 25 | echo "Testing global flow dissector..." 26 | 27 | $bpftool prog loadall $BPF_FILE /sys/fs/bpf/flow \ 28 | type flow_dissector 29 | 30 | if ! unshare --net $bpftool prog attach pinned \ 31 | /sys/fs/bpf/flow/_dissect flow_dissector; then 32 | echo "Unexpected unsuccessful attach in namespace" >&2 33 | err=1 34 | fi 35 | 36 | $bpftool prog attach pinned /sys/fs/bpf/flow/_dissect \ 37 | flow_dissector 38 | 39 | if unshare --net $bpftool prog attach pinned \ 40 | /sys/fs/bpf/flow/_dissect flow_dissector; then 41 | echo "Unexpected successful attach in namespace" >&2 42 | err=1 43 | fi 44 | 45 | if ! $bpftool prog detach pinned \ 46 | /sys/fs/bpf/flow/_dissect flow_dissector; then 47 | echo "Failed to detach flow dissector" >&2 48 | err=1 49 | fi 50 | 51 | rm -rf /sys/fs/bpf/flow 52 | else 53 | echo "Skipping root flow dissector test, bpftool not found" >&2 54 | fi 55 | 56 | # Run the rest of the tests in a net namespace. 57 | ../net/in_netns.sh "$0" "$@" 58 | err=$(( $err + $? )) 59 | 60 | if (( $err == 0 )); then 61 | echo "selftests: $TESTNAME [PASS]"; 62 | else 63 | echo "selftests: $TESTNAME [FAILED]"; 64 | fi 65 | 66 | exit $err 67 | fi 68 | 69 | # Determine selftest success via shell exit code 70 | exit_handler() 71 | { 72 | set +e 73 | 74 | # Cleanup 75 | tc filter del dev lo ingress pref 1337 2> /dev/null 76 | tc qdisc del dev lo ingress 2> /dev/null 77 | ./flow_dissector_load -d 2> /dev/null 78 | if [ $unmount -ne 0 ]; then 79 | umount bpffs 2> /dev/null 80 | fi 81 | } 82 | 83 | # Exit script immediately (well catched by trap handler) if any 84 | # program/thing exits with a non-zero status. 85 | set -e 86 | 87 | # (Use 'trap -l' to list meaning of numbers) 88 | trap exit_handler 0 2 3 6 9 89 | 90 | # Mount BPF file system 91 | if /bin/mount | grep /sys/fs/bpf > /dev/null; then 92 | echo "bpffs already mounted" 93 | else 94 | echo "bpffs not mounted. Mounting..." 95 | unmount=1 96 | /bin/mount bpffs /sys/fs/bpf -t bpf 97 | fi 98 | 99 | # Attach BPF program 100 | ./flow_dissector_load -p $BPF_FILE -s _dissect 101 | 102 | # Setup 103 | tc qdisc add dev lo ingress 104 | echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter 105 | echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter 106 | echo 0 > /proc/sys/net/ipv4/conf/lo/rp_filter 107 | 108 | echo "Testing IPv4..." 109 | # Drops all IP/UDP packets coming from port 9 110 | tc filter add dev lo parent ffff: protocol ip pref 1337 flower ip_proto \ 111 | udp src_port 9 action drop 112 | 113 | # Send 10 IPv4/UDP packets from port 8. Filter should not drop any. 114 | ./test_flow_dissector -i 4 -f 8 115 | # Send 10 IPv4/UDP packets from port 9. Filter should drop all. 116 | ./test_flow_dissector -i 4 -f 9 -F 117 | # Send 10 IPv4/UDP packets from port 10. Filter should not drop any. 118 | ./test_flow_dissector -i 4 -f 10 119 | 120 | echo "Testing IPv4 from 127.0.0.127 (fallback to generic dissector)..." 121 | # Send 10 IPv4/UDP packets from port 8. Filter should not drop any. 122 | ./test_flow_dissector -i 4 -S 127.0.0.127 -f 8 123 | # Send 10 IPv4/UDP packets from port 9. Filter should drop all. 124 | ./test_flow_dissector -i 4 -S 127.0.0.127 -f 9 -F 125 | # Send 10 IPv4/UDP packets from port 10. Filter should not drop any. 126 | ./test_flow_dissector -i 4 -S 127.0.0.127 -f 10 127 | 128 | echo "Testing IPIP..." 129 | # Send 10 IPv4/IPv4/UDP packets from port 8. Filter should not drop any. 130 | ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \ 131 | -D 192.168.0.1 -S 1.1.1.1 -f 8 132 | # Send 10 IPv4/IPv4/UDP packets from port 9. Filter should drop all. 133 | ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \ 134 | -D 192.168.0.1 -S 1.1.1.1 -f 9 -F 135 | # Send 10 IPv4/IPv4/UDP packets from port 10. Filter should not drop any. 136 | ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \ 137 | -D 192.168.0.1 -S 1.1.1.1 -f 10 138 | 139 | echo "Testing IPv4 + GRE..." 140 | # Send 10 IPv4/GRE/IPv4/UDP packets from port 8. Filter should not drop any. 141 | ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \ 142 | -D 192.168.0.1 -S 1.1.1.1 -f 8 143 | # Send 10 IPv4/GRE/IPv4/UDP packets from port 9. Filter should drop all. 144 | ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \ 145 | -D 192.168.0.1 -S 1.1.1.1 -f 9 -F 146 | # Send 10 IPv4/GRE/IPv4/UDP packets from port 10. Filter should not drop any. 147 | ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \ 148 | -D 192.168.0.1 -S 1.1.1.1 -f 10 149 | 150 | tc filter del dev lo ingress pref 1337 151 | 152 | echo "Testing port range..." 153 | # Drops all IP/UDP packets coming from port 8-10 154 | tc filter add dev lo parent ffff: protocol ip pref 1337 flower ip_proto \ 155 | udp src_port 8-10 action drop 156 | 157 | # Send 10 IPv4/UDP packets from port 7. Filter should not drop any. 158 | ./test_flow_dissector -i 4 -f 7 159 | # Send 10 IPv4/UDP packets from port 9. Filter should drop all. 160 | ./test_flow_dissector -i 4 -f 9 -F 161 | # Send 10 IPv4/UDP packets from port 11. Filter should not drop any. 162 | ./test_flow_dissector -i 4 -f 11 163 | 164 | tc filter del dev lo ingress pref 1337 165 | 166 | echo "Testing IPv6..." 167 | # Drops all IPv6/UDP packets coming from port 9 168 | tc filter add dev lo parent ffff: protocol ipv6 pref 1337 flower ip_proto \ 169 | udp src_port 9 action drop 170 | 171 | # Send 10 IPv6/UDP packets from port 8. Filter should not drop any. 172 | ./test_flow_dissector -i 6 -f 8 173 | # Send 10 IPv6/UDP packets from port 9. Filter should drop all. 174 | ./test_flow_dissector -i 6 -f 9 -F 175 | # Send 10 IPv6/UDP packets from port 10. Filter should not drop any. 176 | ./test_flow_dissector -i 6 -f 10 177 | 178 | exit 0 179 | -------------------------------------------------------------------------------- /src/helpers/errno_helpers.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define warn(...) fprintf(stderr, __VA_ARGS__) 10 | 11 | #ifdef __x86_64__ 12 | static int errno_by_name_x86_64(const char *errno_name) 13 | { 14 | 15 | #define strcase(X, N) if (!strcmp(errno_name, (X))) return N 16 | 17 | strcase("EPERM", 1); 18 | strcase("ENOENT", 2); 19 | strcase("ESRCH", 3); 20 | strcase("EINTR", 4); 21 | strcase("EIO", 5); 22 | strcase("ENXIO", 6); 23 | strcase("E2BIG", 7); 24 | strcase("ENOEXEC", 8); 25 | strcase("EBADF", 9); 26 | strcase("ECHILD", 10); 27 | strcase("EAGAIN", 11); 28 | strcase("EWOULDBLOCK", 11); 29 | strcase("ENOMEM", 12); 30 | strcase("EACCES", 13); 31 | strcase("EFAULT", 14); 32 | strcase("ENOTBLK", 15); 33 | strcase("EBUSY", 16); 34 | strcase("EEXIST", 17); 35 | strcase("EXDEV", 18); 36 | strcase("ENODEV", 19); 37 | strcase("ENOTDIR", 20); 38 | strcase("EISDIR", 21); 39 | strcase("EINVAL", 22); 40 | strcase("ENFILE", 23); 41 | strcase("EMFILE", 24); 42 | strcase("ENOTTY", 25); 43 | strcase("ETXTBSY", 26); 44 | strcase("EFBIG", 27); 45 | strcase("ENOSPC", 28); 46 | strcase("ESPIPE", 29); 47 | strcase("EROFS", 30); 48 | strcase("EMLINK", 31); 49 | strcase("EPIPE", 32); 50 | strcase("EDOM", 33); 51 | strcase("ERANGE", 34); 52 | strcase("EDEADLK", 35); 53 | strcase("EDEADLOCK", 35); 54 | strcase("ENAMETOOLONG", 36); 55 | strcase("ENOLCK", 37); 56 | strcase("ENOSYS", 38); 57 | strcase("ENOTEMPTY", 39); 58 | strcase("ELOOP", 40); 59 | strcase("ENOMSG", 42); 60 | strcase("EIDRM", 43); 61 | strcase("ECHRNG", 44); 62 | strcase("EL2NSYNC", 45); 63 | strcase("EL3HLT", 46); 64 | strcase("EL3RST", 47); 65 | strcase("ELNRNG", 48); 66 | strcase("EUNATCH", 49); 67 | strcase("ENOCSI", 50); 68 | strcase("EL2HLT", 51); 69 | strcase("EBADE", 52); 70 | strcase("EBADR", 53); 71 | strcase("EXFULL", 54); 72 | strcase("ENOANO", 55); 73 | strcase("EBADRQC", 56); 74 | strcase("EBADSLT", 57); 75 | strcase("EBFONT", 59); 76 | strcase("ENOSTR", 60); 77 | strcase("ENODATA", 61); 78 | strcase("ETIME", 62); 79 | strcase("ENOSR", 63); 80 | strcase("ENONET", 64); 81 | strcase("ENOPKG", 65); 82 | strcase("EREMOTE", 66); 83 | strcase("ENOLINK", 67); 84 | strcase("EADV", 68); 85 | strcase("ESRMNT", 69); 86 | strcase("ECOMM", 70); 87 | strcase("EPROTO", 71); 88 | strcase("EMULTIHOP", 72); 89 | strcase("EDOTDOT", 73); 90 | strcase("EBADMSG", 74); 91 | strcase("EOVERFLOW", 75); 92 | strcase("ENOTUNIQ", 76); 93 | strcase("EBADFD", 77); 94 | strcase("EREMCHG", 78); 95 | strcase("ELIBACC", 79); 96 | strcase("ELIBBAD", 80); 97 | strcase("ELIBSCN", 81); 98 | strcase("ELIBMAX", 82); 99 | strcase("ELIBEXEC", 83); 100 | strcase("EILSEQ", 84); 101 | strcase("ERESTART", 85); 102 | strcase("ESTRPIPE", 86); 103 | strcase("EUSERS", 87); 104 | strcase("ENOTSOCK", 88); 105 | strcase("EDESTADDRREQ", 89); 106 | strcase("EMSGSIZE", 90); 107 | strcase("EPROTOTYPE", 91); 108 | strcase("ENOPROTOOPT", 92); 109 | strcase("EPROTONOSUPPORT", 93); 110 | strcase("ESOCKTNOSUPPORT", 94); 111 | strcase("ENOTSUP", 95); 112 | strcase("EOPNOTSUPP", 95); 113 | strcase("EPFNOSUPPORT", 96); 114 | strcase("EAFNOSUPPORT", 97); 115 | strcase("EADDRINUSE", 98); 116 | strcase("EADDRNOTAVAIL", 99); 117 | strcase("ENETDOWN", 100); 118 | strcase("ENETUNREACH", 101); 119 | strcase("ENETRESET", 102); 120 | strcase("ECONNABORTED", 103); 121 | strcase("ECONNRESET", 104); 122 | strcase("ENOBUFS", 105); 123 | strcase("EISCONN", 106); 124 | strcase("ENOTCONN", 107); 125 | strcase("ESHUTDOWN", 108); 126 | strcase("ETOOMANYREFS", 109); 127 | strcase("ETIMEDOUT", 110); 128 | strcase("ECONNREFUSED", 111); 129 | strcase("EHOSTDOWN", 112); 130 | strcase("EHOSTUNREACH", 113); 131 | strcase("EALREADY", 114); 132 | strcase("EINPROGRESS", 115); 133 | strcase("ESTALE", 116); 134 | strcase("EUCLEAN", 117); 135 | strcase("ENOTNAM", 118); 136 | strcase("ENAVAIL", 119); 137 | strcase("EISNAM", 120); 138 | strcase("EREMOTEIO", 121); 139 | strcase("EDQUOT", 122); 140 | strcase("ENOMEDIUM", 123); 141 | strcase("EMEDIUMTYPE", 124); 142 | strcase("ECANCELED", 125); 143 | strcase("ENOKEY", 126); 144 | strcase("EKEYEXPIRED", 127); 145 | strcase("EKEYREVOKED", 128); 146 | strcase("EKEYREJECTED", 129); 147 | strcase("EOWNERDEAD", 130); 148 | strcase("ENOTRECOVERABLE", 131); 149 | strcase("ERFKILL", 132); 150 | strcase("EHWPOISON", 133); 151 | 152 | #undef strcase 153 | 154 | return -1; 155 | 156 | } 157 | #endif 158 | 159 | /* Try to find the errno number using the errno(1) program */ 160 | static int errno_by_name_dynamic(const char *errno_name) 161 | { 162 | int i, len = strlen(errno_name); 163 | int err, number = -1; 164 | char buf[128]; 165 | char cmd[64]; 166 | char *end; 167 | long val; 168 | FILE *f; 169 | 170 | /* sanity check to not call popen with random input */ 171 | for (i = 0; i < len; i++) { 172 | if (errno_name[i] < 'A' || errno_name[i] > 'Z') { 173 | warn("errno_name contains invalid char 0x%02x: %s\n", 174 | errno_name[i], errno_name); 175 | return -1; 176 | } 177 | } 178 | 179 | snprintf(cmd, sizeof(cmd), "errno %s", errno_name); 180 | f = popen(cmd, "r"); 181 | if (!f) { 182 | warn("popen: %s: %s\n", cmd, strerror(errno)); 183 | return -1; 184 | } 185 | 186 | if (!fgets(buf, sizeof(buf), f)) { 187 | goto close; 188 | } else if (ferror(f)) { 189 | warn("fgets: %s\n", strerror(errno)); 190 | goto close; 191 | } 192 | 193 | // expecting " " 194 | if (strncmp(errno_name, buf, len) || strlen(buf) < len+2) { 195 | warn("expected '%s': %s\n", errno_name, buf); 196 | goto close; 197 | } 198 | errno = 0; 199 | val = strtol(buf+len+2, &end, 10); 200 | if (errno || end == (buf+len+2) || number < 0 || number > INT_MAX) { 201 | warn("can't parse the second column, expected int: %s\n", buf); 202 | goto close; 203 | } 204 | number = val; 205 | 206 | close: 207 | err = pclose(f); 208 | if (err < 0) 209 | warn("pclose: %s\n", strerror(errno)); 210 | #ifndef __x86_64__ 211 | /* Ignore the error for x86_64 where we have a table compiled in */ 212 | else if (err && WEXITSTATUS(err) == 127) { 213 | warn("errno(1) required for errno name/number mapping\n"); 214 | } else if (err) { 215 | warn("errno(1) exit status (see wait(2)): 0x%x\n", err); 216 | } 217 | #endif 218 | return number; 219 | } 220 | 221 | int errno_by_name(const char *errno_name) 222 | { 223 | #ifdef __x86_64__ 224 | int err; 225 | 226 | err = errno_by_name_x86_64(errno_name); 227 | if (err >= 0) 228 | return err; 229 | #endif 230 | 231 | return errno_by_name_dynamic(errno_name); 232 | } 233 | -------------------------------------------------------------------------------- /src/test_lwt_bpf.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2016 Thomas Graf 2 | * 3 | * This program is free software; you can redistribute it and/or 4 | * modify it under the terms of version 2 of the GNU General Public 5 | * License as published by the Free Software Foundation. 6 | * 7 | * This program is distributed in the hope that it will be useful, but 8 | * WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 | * General Public License for more details. 11 | */ 12 | 13 | #include "vmlinux.h" 14 | #include "net_shared.h" 15 | #include 16 | #include 17 | 18 | # define printk(fmt, ...) \ 19 | ({ \ 20 | char ____fmt[] = fmt; \ 21 | bpf_trace_printk(____fmt, sizeof(____fmt), \ 22 | ##__VA_ARGS__); \ 23 | }) 24 | 25 | #define CB_MAGIC 1234 26 | 27 | /* Test: Pass all packets through */ 28 | SEC("nop") 29 | int do_nop(struct __sk_buff *skb) 30 | { 31 | return BPF_OK; 32 | } 33 | 34 | /* Test: Verify context information can be accessed */ 35 | SEC("test_ctx") 36 | int do_test_ctx(struct __sk_buff *skb) 37 | { 38 | skb->cb[0] = CB_MAGIC; 39 | printk("len %d hash %d protocol %d", skb->len, skb->hash, 40 | skb->protocol); 41 | printk("cb %d ingress_ifindex %d ifindex %d", skb->cb[0], 42 | skb->ingress_ifindex, skb->ifindex); 43 | 44 | return BPF_OK; 45 | } 46 | 47 | /* Test: Ensure skb->cb[] buffer is cleared */ 48 | SEC("test_cb") 49 | int do_test_cb(struct __sk_buff *skb) 50 | { 51 | printk("cb0: %x cb1: %x cb2: %x", skb->cb[0], skb->cb[1], 52 | skb->cb[2]); 53 | printk("cb3: %x cb4: %x", skb->cb[3], skb->cb[4]); 54 | 55 | return BPF_OK; 56 | } 57 | 58 | /* Test: Verify skb data can be read */ 59 | SEC("test_data") 60 | int do_test_data(struct __sk_buff *skb) 61 | { 62 | void *data = (void *)(long)skb->data; 63 | void *data_end = (void *)(long)skb->data_end; 64 | struct iphdr *iph = data; 65 | 66 | if (data + sizeof(*iph) > data_end) { 67 | printk("packet truncated"); 68 | return BPF_DROP; 69 | } 70 | 71 | printk("src: %x dst: %x", iph->saddr, iph->daddr); 72 | 73 | return BPF_OK; 74 | } 75 | 76 | #define IP_CSUM_OFF offsetof(struct iphdr, check) 77 | #define IP_DST_OFF offsetof(struct iphdr, daddr) 78 | #define IP_SRC_OFF offsetof(struct iphdr, saddr) 79 | #define IP_PROTO_OFF offsetof(struct iphdr, protocol) 80 | #define TCP_CSUM_OFF offsetof(struct tcphdr, check) 81 | #define UDP_CSUM_OFF offsetof(struct udphdr, check) 82 | #define IS_PSEUDO 0x10 83 | 84 | static inline int rewrite(struct __sk_buff *skb, uint32_t old_ip, 85 | uint32_t new_ip, int rw_daddr) 86 | { 87 | int ret, off = 0, flags = IS_PSEUDO; 88 | uint8_t proto; 89 | 90 | ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1); 91 | if (ret < 0) { 92 | printk("bpf_l4_csum_replace failed: %d", ret); 93 | return BPF_DROP; 94 | } 95 | 96 | switch (proto) { 97 | case IPPROTO_TCP: 98 | off = TCP_CSUM_OFF; 99 | break; 100 | 101 | case IPPROTO_UDP: 102 | off = UDP_CSUM_OFF; 103 | flags |= BPF_F_MARK_MANGLED_0; 104 | break; 105 | 106 | case IPPROTO_ICMPV6: 107 | off = offsetof(struct icmp6hdr, icmp6_cksum); 108 | break; 109 | } 110 | 111 | if (off) { 112 | ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip, 113 | flags | sizeof(new_ip)); 114 | if (ret < 0) { 115 | printk("bpf_l4_csum_replace failed: %d"); 116 | return BPF_DROP; 117 | } 118 | } 119 | 120 | ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip)); 121 | if (ret < 0) { 122 | printk("bpf_l3_csum_replace failed: %d", ret); 123 | return BPF_DROP; 124 | } 125 | 126 | if (rw_daddr) 127 | ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0); 128 | else 129 | ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0); 130 | 131 | if (ret < 0) { 132 | printk("bpf_skb_store_bytes() failed: %d", ret); 133 | return BPF_DROP; 134 | } 135 | 136 | return BPF_OK; 137 | } 138 | 139 | /* Test: Verify skb data can be modified */ 140 | SEC("test_rewrite") 141 | int do_test_rewrite(struct __sk_buff *skb) 142 | { 143 | uint32_t old_ip, new_ip = 0x3fea8c0; 144 | int ret; 145 | 146 | ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4); 147 | if (ret < 0) { 148 | printk("bpf_skb_load_bytes failed: %d", ret); 149 | return BPF_DROP; 150 | } 151 | 152 | if (old_ip == 0x2fea8c0) { 153 | printk("out: rewriting from %x to %x", old_ip, new_ip); 154 | return rewrite(skb, old_ip, new_ip, 1); 155 | } 156 | 157 | return BPF_OK; 158 | } 159 | 160 | static inline int __do_push_ll_and_redirect(struct __sk_buff *skb) 161 | { 162 | uint64_t smac = SRC_MAC, dmac = DST_MAC; 163 | int ret, ifindex = DST_IFINDEX; 164 | struct ethhdr ehdr; 165 | 166 | ret = bpf_skb_change_head(skb, 14, 0); 167 | if (ret < 0) { 168 | printk("skb_change_head() failed: %d", ret); 169 | } 170 | 171 | ehdr.h_proto = bpf_htons(ETH_P_IP); 172 | memcpy(&ehdr.h_source, &smac, 6); 173 | memcpy(&ehdr.h_dest, &dmac, 6); 174 | 175 | ret = bpf_skb_store_bytes(skb, 0, &ehdr, sizeof(ehdr), 0); 176 | if (ret < 0) { 177 | printk("skb_store_bytes() failed: %d", ret); 178 | return BPF_DROP; 179 | } 180 | 181 | return bpf_redirect(ifindex, 0); 182 | } 183 | 184 | SEC("push_ll_and_redirect_silent") 185 | int do_push_ll_and_redirect_silent(struct __sk_buff *skb) 186 | { 187 | return __do_push_ll_and_redirect(skb); 188 | } 189 | 190 | SEC("push_ll_and_redirect") 191 | int do_push_ll_and_redirect(struct __sk_buff *skb) 192 | { 193 | int ret, ifindex = DST_IFINDEX; 194 | 195 | ret = __do_push_ll_and_redirect(skb); 196 | if (ret >= 0) 197 | printk("redirected to %d", ifindex); 198 | 199 | return ret; 200 | } 201 | 202 | static inline void __fill_garbage(struct __sk_buff *skb) 203 | { 204 | uint64_t f = 0xFFFFFFFFFFFFFFFF; 205 | 206 | bpf_skb_store_bytes(skb, 0, &f, sizeof(f), 0); 207 | bpf_skb_store_bytes(skb, 8, &f, sizeof(f), 0); 208 | bpf_skb_store_bytes(skb, 16, &f, sizeof(f), 0); 209 | bpf_skb_store_bytes(skb, 24, &f, sizeof(f), 0); 210 | bpf_skb_store_bytes(skb, 32, &f, sizeof(f), 0); 211 | bpf_skb_store_bytes(skb, 40, &f, sizeof(f), 0); 212 | bpf_skb_store_bytes(skb, 48, &f, sizeof(f), 0); 213 | bpf_skb_store_bytes(skb, 56, &f, sizeof(f), 0); 214 | bpf_skb_store_bytes(skb, 64, &f, sizeof(f), 0); 215 | bpf_skb_store_bytes(skb, 72, &f, sizeof(f), 0); 216 | bpf_skb_store_bytes(skb, 80, &f, sizeof(f), 0); 217 | bpf_skb_store_bytes(skb, 88, &f, sizeof(f), 0); 218 | } 219 | 220 | SEC("fill_garbage") 221 | int do_fill_garbage(struct __sk_buff *skb) 222 | { 223 | __fill_garbage(skb); 224 | printk("Set initial 96 bytes of header to FF"); 225 | return BPF_OK; 226 | } 227 | 228 | SEC("fill_garbage_and_redirect") 229 | int do_fill_garbage_and_redirect(struct __sk_buff *skb) 230 | { 231 | int ifindex = DST_IFINDEX; 232 | __fill_garbage(skb); 233 | printk("redirected to %d", ifindex); 234 | return bpf_redirect(ifindex, 0); 235 | } 236 | 237 | /* Drop all packets */ 238 | SEC("drop_all") 239 | int do_drop_all(struct __sk_buff *skb) 240 | { 241 | printk("dropping with: %d", BPF_DROP); 242 | return BPF_DROP; 243 | } 244 | 245 | char _license[] SEC("license") = "GPL"; 246 | -------------------------------------------------------------------------------- /tools/cmake/FindBpfObject.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | #[=======================================================================[.rst: 4 | FindBpfObject 5 | -------- 6 | 7 | Find BpfObject 8 | 9 | This module finds if all the dependencies for eBPF Compile-Once-Run-Everywhere 10 | programs are available and where all the components are located. 11 | 12 | The caller may set the following variables to disable automatic 13 | search/processing for the associated component: 14 | 15 | ``BPFOBJECT_BPFTOOL_EXE`` 16 | Path to ``bpftool`` binary 17 | 18 | ``BPFOBJECT_CLANG_EXE`` 19 | Path to ``clang`` binary 20 | 21 | ``LIBBPF_INCLUDE_DIRS`` 22 | Path to ``libbpf`` development headers 23 | 24 | ``LIBBPF_LIBRARIES`` 25 | Path to `libbpf` library 26 | 27 | ``BPFOBJECT_VMLINUX_H`` 28 | Path to ``vmlinux.h`` generated by ``bpftool``. If unset, this module will 29 | attempt to automatically generate a copy. 30 | 31 | This module sets the following result variables: 32 | 33 | :: 34 | 35 | BpfObject_FOUND = TRUE if all components are found 36 | 37 | 38 | This module also provides the ``bpf_object()`` macro. This macro generates a 39 | cmake interface library for the BPF object's generated skeleton as well 40 | as the associated dependencies. 41 | 42 | .. code-block:: cmake 43 | 44 | bpf_object( ) 45 | 46 | Given an abstract ```` for a BPF object and the associated ```` 47 | file, generates an interface library target, ``_skel``, that may be 48 | linked against by other cmake targets. 49 | 50 | Example Usage: 51 | 52 | :: 53 | 54 | find_package(BpfObject REQUIRED) 55 | bpf_object(myobject myobject.bpf.c) 56 | add_executable(myapp myapp.c) 57 | target_link_libraries(myapp myobject_skel) 58 | 59 | #]=======================================================================] 60 | 61 | if(NOT BPFOBJECT_BPFTOOL_EXE) 62 | find_program(BPFOBJECT_BPFTOOL_EXE NAMES bpftool DOC "Path to bpftool executable") 63 | endif() 64 | 65 | if(NOT BPFOBJECT_CLANG_EXE) 66 | find_program(BPFOBJECT_CLANG_EXE NAMES clang DOC "Path to clang executable") 67 | 68 | execute_process(COMMAND ${BPFOBJECT_CLANG_EXE} --version 69 | OUTPUT_VARIABLE CLANG_version_output 70 | ERROR_VARIABLE CLANG_version_error 71 | RESULT_VARIABLE CLANG_version_result 72 | OUTPUT_STRIP_TRAILING_WHITESPACE) 73 | 74 | # Check that clang is new enough 75 | if(${CLANG_version_result} EQUAL 0) 76 | if("${CLANG_version_output}" MATCHES "clang version ([^\n]+)\n") 77 | # Transform X.Y.Z into X;Y;Z which can then be interpreted as a list 78 | set(CLANG_VERSION "${CMAKE_MATCH_1}") 79 | string(REPLACE "." ";" CLANG_VERSION_LIST ${CLANG_VERSION}) 80 | list(GET CLANG_VERSION_LIST 0 CLANG_VERSION_MAJOR) 81 | 82 | # Anything older than clang 10 doesn't really work 83 | string(COMPARE LESS ${CLANG_VERSION_MAJOR} 10 CLANG_VERSION_MAJOR_LT10) 84 | if(${CLANG_VERSION_MAJOR_LT10}) 85 | message(FATAL_ERROR "clang ${CLANG_VERSION} is too old for BPF CO-RE") 86 | endif() 87 | 88 | message(STATUS "Found clang version: ${CLANG_VERSION}") 89 | else() 90 | message(FATAL_ERROR "Failed to parse clang version string: ${CLANG_version_output}") 91 | endif() 92 | else() 93 | message(FATAL_ERROR "Command \"${BPFOBJECT_CLANG_EXE} --version\" failed with output:\n${CLANG_version_error}") 94 | endif() 95 | endif() 96 | 97 | if(NOT LIBBPF_INCLUDE_DIRS OR NOT LIBBPF_LIBRARIES) 98 | find_package(LibBpf) 99 | endif() 100 | 101 | if(BPFOBJECT_VMLINUX_H) 102 | get_filename_component(GENERATED_VMLINUX_DIR ${BPFOBJECT_VMLINUX_H} DIRECTORY) 103 | elseif(BPFOBJECT_BPFTOOL_EXE) 104 | # Generate vmlinux.h 105 | set(GENERATED_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR}) 106 | set(BPFOBJECT_VMLINUX_H ${GENERATED_VMLINUX_DIR}/vmlinux.h) 107 | execute_process(COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c 108 | OUTPUT_FILE ${BPFOBJECT_VMLINUX_H} 109 | ERROR_VARIABLE VMLINUX_error 110 | RESULT_VARIABLE VMLINUX_result) 111 | if(${VMLINUX_result} EQUAL 0) 112 | set(VMLINUX ${BPFOBJECT_VMLINUX_H}) 113 | else() 114 | message(FATAL_ERROR "Failed to dump vmlinux.h from BTF: ${VMLINUX_error}") 115 | endif() 116 | endif() 117 | 118 | include(FindPackageHandleStandardArgs) 119 | find_package_handle_standard_args(BpfObject 120 | REQUIRED_VARS 121 | BPFOBJECT_BPFTOOL_EXE 122 | BPFOBJECT_CLANG_EXE 123 | LIBBPF_INCLUDE_DIRS 124 | LIBBPF_LIBRARIES 125 | GENERATED_VMLINUX_DIR) 126 | 127 | # Get clang bpf system includes 128 | execute_process( 129 | COMMAND bash -c "${BPFOBJECT_CLANG_EXE} -v -E - < /dev/null 2>&1 | 130 | sed -n '/<...> search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }'" 131 | OUTPUT_VARIABLE CLANG_SYSTEM_INCLUDES_output 132 | ERROR_VARIABLE CLANG_SYSTEM_INCLUDES_error 133 | RESULT_VARIABLE CLANG_SYSTEM_INCLUDES_result 134 | OUTPUT_STRIP_TRAILING_WHITESPACE) 135 | if(${CLANG_SYSTEM_INCLUDES_result} EQUAL 0) 136 | separate_arguments(CLANG_SYSTEM_INCLUDES UNIX_COMMAND ${CLANG_SYSTEM_INCLUDES_output}) 137 | message(STATUS "BPF system include flags: ${CLANG_SYSTEM_INCLUDES}") 138 | else() 139 | message(FATAL_ERROR "Failed to determine BPF system includes: ${CLANG_SYSTEM_INCLUDES_error}") 140 | endif() 141 | 142 | # Get target arch 143 | execute_process(COMMAND uname -m 144 | COMMAND sed -e "s/x86_64/x86/" -e "s/aarch64/arm64/" -e "s/ppc64le/powerpc/" -e "s/mips.*/mips/" 145 | OUTPUT_VARIABLE ARCH_output 146 | ERROR_VARIABLE ARCH_error 147 | RESULT_VARIABLE ARCH_result 148 | OUTPUT_STRIP_TRAILING_WHITESPACE) 149 | if(${ARCH_result} EQUAL 0) 150 | set(ARCH ${ARCH_output}) 151 | message(STATUS "BPF target arch: ${ARCH}") 152 | else() 153 | message(FATAL_ERROR "Failed to determine target architecture: ${ARCH_error}") 154 | endif() 155 | 156 | # Public macro 157 | macro(bpf_object name input) 158 | set(BPF_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${input}) 159 | set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.bpf.o) 160 | set(BPF_SKEL_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.skel.h) 161 | set(OUTPUT_TARGET ${name}_skel) 162 | 163 | # Build BPF object file 164 | add_custom_command(OUTPUT ${BPF_O_FILE} 165 | COMMAND ${BPFOBJECT_CLANG_EXE} -g -O2 -target bpf -D__TARGET_ARCH_${ARCH} 166 | ${CLANG_SYSTEM_INCLUDES} -I${GENERATED_VMLINUX_DIR} 167 | -isystem ${LIBBPF_INCLUDE_DIRS} -c ${BPF_C_FILE} -o ${BPF_O_FILE} 168 | COMMAND_EXPAND_LISTS 169 | VERBATIM 170 | DEPENDS ${BPF_C_FILE} 171 | COMMENT "[clang] Building BPF object: ${name}") 172 | 173 | # Build BPF skeleton header 174 | add_custom_command(OUTPUT ${BPF_SKEL_FILE} 175 | COMMAND bash -c "${BPFOBJECT_BPFTOOL_EXE} gen skeleton ${BPF_O_FILE} > ${BPF_SKEL_FILE}" 176 | VERBATIM 177 | DEPENDS ${BPF_O_FILE} 178 | COMMENT "[skel] Building BPF skeleton: ${name}") 179 | 180 | add_library(${OUTPUT_TARGET} INTERFACE) 181 | target_sources(${OUTPUT_TARGET} INTERFACE ${BPF_SKEL_FILE}) 182 | target_include_directories(${OUTPUT_TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) 183 | target_include_directories(${OUTPUT_TARGET} SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS}) 184 | target_link_libraries(${OUTPUT_TARGET} INTERFACE ${LIBBPF_LIBRARIES} -lelf -lz) 185 | endmacro() 186 | -------------------------------------------------------------------------------- /src/softirqs.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Wenbo Zhang 3 | // 4 | // Based on softirq(8) from BCC by Brendan Gregg & Sasha Goldshtein. 5 | // 15-Aug-2020 Wenbo Zhang Created this. 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "softirqs.h" 16 | #include "softirqs.skel.h" 17 | #include "trace_helpers.h" 18 | 19 | struct env { 20 | bool distributed; 21 | bool nanoseconds; 22 | bool count; 23 | time_t interval; 24 | int times; 25 | bool timestamp; 26 | bool verbose; 27 | } env = { 28 | .interval = 99999999, 29 | .times = 99999999, 30 | .count = false, 31 | }; 32 | 33 | static volatile bool exiting; 34 | 35 | const char *argp_program_version = "softirqs 0.1"; 36 | const char *argp_program_bug_address = 37 | "https://github.com/iovisor/bcc/tree/master/libbpf-tools"; 38 | const char argp_program_doc[] = 39 | "Summarize soft irq event time as histograms.\n" 40 | "\n" 41 | "USAGE: softirqs [--help] [-T] [-N] [-d] [interval] [count]\n" 42 | "\n" 43 | "EXAMPLES:\n" 44 | " softirqs # sum soft irq event time\n" 45 | " softirqs -d # show soft irq event time as histograms\n" 46 | " softirqs 1 10 # print 1 second summaries, 10 times\n" 47 | " softirqs -NT 1 # 1s summaries, nanoseconds, and timestamps\n"; 48 | 49 | static const struct argp_option opts[] = { 50 | { "distributed", 'd', NULL, 0, "Show distributions as histograms" }, 51 | { "timestamp", 'T', NULL, 0, "Include timestamp on output" }, 52 | { "nanoseconds", 'N', NULL, 0, "Output in nanoseconds" }, 53 | { "count", 'C', NULL, 0, "Show event counts with timing" }, 54 | { "verbose", 'v', NULL, 0, "Verbose debug output" }, 55 | { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, 56 | {}, 57 | }; 58 | 59 | static error_t parse_arg(int key, char *arg, struct argp_state *state) 60 | { 61 | static int pos_args; 62 | 63 | switch (key) { 64 | case 'h': 65 | argp_state_help(state, stderr, ARGP_HELP_STD_HELP); 66 | break; 67 | case 'v': 68 | env.verbose = true; 69 | break; 70 | case 'd': 71 | env.distributed = true; 72 | break; 73 | case 'N': 74 | env.nanoseconds = true; 75 | break; 76 | case 'T': 77 | env.timestamp = true; 78 | break; 79 | case 'C': 80 | env.count = true; 81 | break; 82 | case ARGP_KEY_ARG: 83 | errno = 0; 84 | if (pos_args == 0) { 85 | env.interval = strtol(arg, NULL, 10); 86 | if (errno) { 87 | fprintf(stderr, "invalid internal\n"); 88 | argp_usage(state); 89 | } 90 | } else if (pos_args == 1) { 91 | env.times = strtol(arg, NULL, 10); 92 | if (errno) { 93 | fprintf(stderr, "invalid times\n"); 94 | argp_usage(state); 95 | } 96 | } else { 97 | fprintf(stderr, 98 | "unrecognized positional argument: %s\n", arg); 99 | argp_usage(state); 100 | } 101 | pos_args++; 102 | break; 103 | default: 104 | return ARGP_ERR_UNKNOWN; 105 | } 106 | return 0; 107 | } 108 | 109 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 110 | { 111 | if (level == LIBBPF_DEBUG && !env.verbose) 112 | return 0; 113 | return vfprintf(stderr, format, args); 114 | } 115 | 116 | static void sig_handler(int sig) 117 | { 118 | exiting = true; 119 | } 120 | 121 | enum { 122 | HI_SOFTIRQ = 0, 123 | TIMER_SOFTIRQ = 1, 124 | NET_TX_SOFTIRQ = 2, 125 | NET_RX_SOFTIRQ = 3, 126 | BLOCK_SOFTIRQ = 4, 127 | IRQ_POLL_SOFTIRQ = 5, 128 | TASKLET_SOFTIRQ = 6, 129 | SCHED_SOFTIRQ = 7, 130 | HRTIMER_SOFTIRQ = 8, 131 | RCU_SOFTIRQ = 9, 132 | NR_SOFTIRQS = 10, 133 | }; 134 | 135 | static char *vec_names[] = { 136 | [HI_SOFTIRQ] = "hi", 137 | [TIMER_SOFTIRQ] = "timer", 138 | [NET_TX_SOFTIRQ] = "net_tx", 139 | [NET_RX_SOFTIRQ] = "net_rx", 140 | [BLOCK_SOFTIRQ] = "block", 141 | [IRQ_POLL_SOFTIRQ] = "irq_poll", 142 | [TASKLET_SOFTIRQ] = "tasklet", 143 | [SCHED_SOFTIRQ] = "sched", 144 | [HRTIMER_SOFTIRQ] = "hrtimer", 145 | [RCU_SOFTIRQ] = "rcu", 146 | }; 147 | 148 | static int print_count(struct softirqs_bpf__bss *bss) 149 | { 150 | const char *units = env.nanoseconds ? "nsecs" : "usecs"; 151 | __u64 count, time; 152 | __u32 vec; 153 | 154 | printf("%-16s %-6s%-5s %-11s\n", "SOFTIRQ", "TOTAL_", 155 | units, env.count?"TOTAL_count":""); 156 | 157 | for (vec = 0; vec < NR_SOFTIRQS; vec++) { 158 | time = __atomic_exchange_n(&bss->time[vec], 0, 159 | __ATOMIC_RELAXED); 160 | count = __atomic_exchange_n(&bss->counts[vec], 0, 161 | __ATOMIC_RELAXED); 162 | if (count > 0) { 163 | printf("%-16s %11llu", vec_names[vec], time); 164 | if (env.count) { 165 | printf(" %11llu", count); 166 | } 167 | printf("\n"); 168 | } 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | static struct hist zero; 175 | 176 | static int print_hist(struct softirqs_bpf__bss *bss) 177 | { 178 | const char *units = env.nanoseconds ? "nsecs" : "usecs"; 179 | __u32 vec; 180 | 181 | for (vec = 0; vec < NR_SOFTIRQS; vec++) { 182 | struct hist hist = bss->hists[vec]; 183 | 184 | bss->hists[vec] = zero; 185 | if (!memcmp(&zero, &hist, sizeof(hist))) 186 | continue; 187 | printf("softirq = %s\n", vec_names[vec]); 188 | print_log2_hist(hist.slots, MAX_SLOTS, units); 189 | printf("\n"); 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | int main(int argc, char **argv) 196 | { 197 | static const struct argp argp = { 198 | .options = opts, 199 | .parser = parse_arg, 200 | .doc = argp_program_doc, 201 | }; 202 | struct softirqs_bpf *obj; 203 | struct tm *tm; 204 | char ts[32]; 205 | time_t t; 206 | int err; 207 | 208 | err = argp_parse(&argp, argc, argv, 0, NULL, NULL); 209 | if (err) 210 | return err; 211 | 212 | libbpf_set_print(libbpf_print_fn); 213 | 214 | obj = softirqs_bpf__open(); 215 | if (!obj) { 216 | fprintf(stderr, "failed to open BPF object\n"); 217 | return 1; 218 | } 219 | 220 | if (probe_tp_btf("softirq_entry")) { 221 | bpf_program__set_autoload(obj->progs.softirq_entry, false); 222 | bpf_program__set_autoload(obj->progs.softirq_exit, false); 223 | } else { 224 | bpf_program__set_autoload(obj->progs.softirq_entry_btf, false); 225 | bpf_program__set_autoload(obj->progs.softirq_exit_btf, false); 226 | } 227 | 228 | /* initialize global data (filtering options) */ 229 | obj->rodata->targ_dist = env.distributed; 230 | obj->rodata->targ_ns = env.nanoseconds; 231 | 232 | err = softirqs_bpf__load(obj); 233 | if (err) { 234 | fprintf(stderr, "failed to load BPF object: %d\n", err); 235 | goto cleanup; 236 | } 237 | 238 | if (!obj->bss) { 239 | fprintf(stderr, "Memory-mapping BPF maps is supported starting from Linux 5.7, please upgrade.\n"); 240 | goto cleanup; 241 | } 242 | 243 | err = softirqs_bpf__attach(obj); 244 | if (err) { 245 | fprintf(stderr, "failed to attach BPF programs\n"); 246 | goto cleanup; 247 | } 248 | 249 | signal(SIGINT, sig_handler); 250 | 251 | printf("Tracing soft irq event time... Hit Ctrl-C to end.\n"); 252 | 253 | /* main: poll */ 254 | while (1) { 255 | sleep(env.interval); 256 | printf("\n"); 257 | 258 | if (env.timestamp) { 259 | time(&t); 260 | tm = localtime(&t); 261 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 262 | printf("%-8s\n", ts); 263 | } 264 | 265 | if (!env.distributed) 266 | err = print_count(obj->bss); 267 | else 268 | err = print_hist(obj->bss); 269 | if (err) 270 | break; 271 | 272 | if (exiting || --env.times == 0) 273 | break; 274 | } 275 | 276 | cleanup: 277 | softirqs_bpf__destroy(obj); 278 | 279 | return err != 0; 280 | } 281 | -------------------------------------------------------------------------------- /src/iptables_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "iptables_test.h" 22 | #include "iptables_test.skel.h" 23 | 24 | #define PORT 8888 25 | 26 | struct iptables_test_bpf *skel; 27 | int map_fd; 28 | bool test_finish; 29 | char temp_dir[30]; 30 | char file[50]; 31 | 32 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 33 | { 34 | return vfprintf(stderr, format, args); 35 | } 36 | 37 | static void prog_attach_iptables() 38 | { 39 | int ret; 40 | char rules[256]; 41 | char template[] = "/tmp/bpf.XXXXXX"; 42 | char *temp_ = mkdtemp(template); 43 | if (temp_ == NULL) 44 | error(1, errno, "make temp dir"); 45 | snprintf(temp_dir, sizeof(temp_dir), "%s", temp_); 46 | 47 | ret = mount(temp_dir, temp_dir, "bpf", 0, NULL); 48 | if (ret) 49 | error(1, errno, "mount bpf"); 50 | 51 | snprintf(file, sizeof(file), "%s/bpf_prog", temp_dir); 52 | 53 | if (bpf_program__pin(skel->progs.iptables_accepted, file)) 54 | error(1, errno, "bpf_obj_pin"); 55 | 56 | ret = snprintf(rules, sizeof(rules), 57 | "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT", file); 58 | if (ret < 0 || ret >= sizeof(rules)) 59 | { 60 | printf("error constructing iptables command\n"); 61 | exit(1); 62 | } 63 | ret = system(rules); 64 | if (ret < 0) 65 | { 66 | printf("iptables rule update failed: %d/n", WEXITSTATUS(ret)); 67 | exit(1); 68 | } 69 | } 70 | 71 | static void prog_detach_iptables() 72 | { 73 | int ret; 74 | char rules[256]; 75 | ret = snprintf(rules, sizeof(rules), 76 | "iptables -D OUTPUT -m bpf --object-pinned %s -j ACCEPT", file); 77 | if (ret < 0 || ret >= sizeof(rules)) 78 | { 79 | printf("error constructing iptables command\n"); 80 | exit(1); 81 | } 82 | ret = system(rules); 83 | if (ret < 0) 84 | { 85 | printf("iptables rule delete failed: %d/n", WEXITSTATUS(ret)); 86 | exit(1); 87 | } 88 | 89 | ret = bpf_program__unpin(skel->progs.iptables_accepted, file); 90 | if (ret) 91 | printf("error unpin bpf prog\n"); 92 | 93 | ret = umount(temp_dir); 94 | if (ret) 95 | printf("error umount bpf. errno:%d\n", errno); 96 | 97 | ret = rmdir(temp_dir); 98 | if (ret) 99 | printf("error rmdir temp dir. ret:%d, errno:%d\n", ret, errno); 100 | } 101 | 102 | static void print_table(void) 103 | { 104 | struct stats curEntry; 105 | uint32_t curN = UINT32_MAX; 106 | uint32_t nextN; 107 | int res; 108 | 109 | while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) 110 | { 111 | curN = nextN; 112 | res = bpf_map_lookup_elem(map_fd, &curN, &curEntry); 113 | if (res < 0) 114 | { 115 | error(1, errno, "fail to get entry value of Key: %u\n", curN); 116 | } 117 | else 118 | { 119 | printf("cookie: %u, uid: 0x%x, Packet Count: %lu," 120 | " Bytes Count: %lu\n", 121 | curN, curEntry.uid, 122 | curEntry.packets, curEntry.bytes); 123 | } 124 | } 125 | } 126 | 127 | static void udp_client(void) 128 | { 129 | struct sockaddr_in si_other = {0}; 130 | struct sockaddr_in si_me = {0}; 131 | struct stats dataEntry; 132 | int s_rcv, s_send, i, recv_len; 133 | char message = 'a'; 134 | char buf; 135 | uint64_t cookie; 136 | int res; 137 | socklen_t cookie_len = sizeof(cookie); 138 | socklen_t slen = sizeof(si_other); 139 | 140 | s_rcv = socket(PF_INET, SOCK_DGRAM, 0); 141 | if (s_rcv < 0) 142 | error(1, errno, "rcv socket creat failed!\n"); 143 | si_other.sin_family = AF_INET; 144 | si_other.sin_port = htons(PORT); 145 | if (inet_aton("127.0.0.1", &si_other.sin_addr) == 0) 146 | error(1, errno, "inet_aton\n"); 147 | if (bind(s_rcv, (struct sockaddr *)&si_other, sizeof(si_other)) == -1) 148 | error(1, errno, "bind\n"); 149 | s_send = socket(PF_INET, SOCK_DGRAM, 0); 150 | if (s_send < 0) 151 | error(1, errno, "send socket creat failed!\n"); 152 | res = getsockopt(s_send, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len); 153 | if (res < 0) 154 | printf("get cookie failed: %s\n", strerror(errno)); 155 | res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry); 156 | // if (res != -1) 157 | // error(1, errno, "socket stat found while flow not active\n"); 158 | for (i = 0; i < 10; i++) 159 | { 160 | res = sendto(s_send, &message, sizeof(message), 0, 161 | (struct sockaddr *)&si_other, slen); 162 | if (res == -1) 163 | error(1, errno, "send\n"); 164 | if (res != sizeof(message)) 165 | error(1, 0, "%uB != %luB\n", res, sizeof(message)); 166 | recv_len = recvfrom(s_rcv, &buf, sizeof(buf), 0, 167 | (struct sockaddr *)&si_me, &slen); 168 | if (recv_len < 0) 169 | error(1, errno, "receive\n"); 170 | res = memcmp(&(si_other.sin_addr), &(si_me.sin_addr), 171 | sizeof(si_me.sin_addr)); 172 | if (res != 0) 173 | error(1, EFAULT, "sender addr error: %d\n", res); 174 | printf("Message received: %c\n", buf); 175 | res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry); 176 | if (res < 0) 177 | error(1, errno, "lookup sk stat failed, cookie: %lu\n", 178 | cookie); 179 | printf("cookie: %lu, uid: 0x%x, Packet Count: %lu," 180 | " Bytes Count: %lu\n\n", 181 | cookie, dataEntry.uid, 182 | dataEntry.packets, dataEntry.bytes); 183 | } 184 | close(s_send); 185 | close(s_rcv); 186 | } 187 | 188 | static void finish(int ret) 189 | { 190 | test_finish = true; 191 | } 192 | 193 | int main(int argc, char *argv[]) 194 | { 195 | int opt; 196 | bool cfg_test_traffic = false; 197 | bool cfg_test_cookie = false; 198 | while ((opt = getopt(argc, argv, "ts")) != -1) 199 | { 200 | switch (opt) 201 | { 202 | case 't': 203 | cfg_test_traffic = true; 204 | break; 205 | case 's': 206 | cfg_test_cookie = true; 207 | break; 208 | } 209 | } 210 | if (!cfg_test_cookie && !cfg_test_traffic) 211 | cfg_test_traffic = true; 212 | 213 | int err; 214 | /* Set up libbpf errors and debug info callback */ 215 | libbpf_set_print(libbpf_print_fn); 216 | 217 | /* Open load and verify BPF application */ 218 | skel = iptables_test_bpf__open_and_load(); 219 | if (!skel) 220 | { 221 | fprintf(stderr, "Failed to open BPF skeleton\n"); 222 | return 1; 223 | } 224 | map_fd = bpf_map__fd(skel->maps.cookie_stats); 225 | 226 | prog_attach_iptables(); 227 | if (cfg_test_traffic) 228 | { 229 | if (signal(SIGINT, finish) == SIG_ERR) 230 | error(1, errno, "register SIGINT handler failed"); 231 | if (signal(SIGTERM, finish) == SIG_ERR) 232 | error(1, errno, "register SIGTERM handler failed"); 233 | while (!test_finish) 234 | { 235 | print_table(); 236 | printf("\n"); 237 | sleep(1); 238 | } 239 | } 240 | else if (cfg_test_cookie) 241 | { 242 | udp_client(); 243 | } 244 | 245 | cleanup: 246 | prog_detach_iptables(); 247 | iptables_test_bpf__destroy(skel); 248 | return -err; 249 | } -------------------------------------------------------------------------------- /src/uprobe_helpers.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Google LLC. */ 3 | #ifndef _GNU_SOURCE 4 | #define _GNU_SOURCE 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define warn(...) fprintf(stderr, __VA_ARGS__) 19 | 20 | /* 21 | * Returns 0 on success; -1 on failure. On sucess, returns via `path` the full 22 | * path to the program for pid. 23 | */ 24 | int get_pid_binary_path(pid_t pid, char *path, size_t path_sz) 25 | { 26 | ssize_t ret; 27 | char proc_pid_exe[32]; 28 | 29 | if (snprintf(proc_pid_exe, sizeof(proc_pid_exe), "/proc/%d/exe", pid) 30 | >= sizeof(proc_pid_exe)) { 31 | warn("snprintf /proc/PID/exe failed"); 32 | return -1; 33 | } 34 | ret = readlink(proc_pid_exe, path, path_sz); 35 | if (ret < 0) { 36 | warn("No such pid %d\n", pid); 37 | return -1; 38 | } 39 | if (ret >= path_sz) { 40 | warn("readlink truncation"); 41 | return -1; 42 | } 43 | path[ret] = '\0'; 44 | 45 | return 0; 46 | } 47 | 48 | /* 49 | * Returns 0 on success; -1 on failure. On success, returns via `path` the full 50 | * path to a library matching the name `lib` that is loaded into pid's address 51 | * space. 52 | */ 53 | int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz) 54 | { 55 | FILE *maps; 56 | char *p; 57 | char proc_pid_maps[32]; 58 | char line_buf[1024]; 59 | char path_buf[1024]; 60 | 61 | if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid) 62 | >= sizeof(proc_pid_maps)) { 63 | warn("snprintf /proc/PID/maps failed"); 64 | return -1; 65 | } 66 | maps = fopen(proc_pid_maps, "r"); 67 | if (!maps) { 68 | warn("No such pid %d\n", pid); 69 | return -1; 70 | } 71 | while (fgets(line_buf, sizeof(line_buf), maps)) { 72 | if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path_buf) != 1) 73 | continue; 74 | /* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */ 75 | p = strrchr(path_buf, '/'); 76 | if (!p) 77 | continue; 78 | if (strncmp(p, "/lib", 4)) 79 | continue; 80 | p += 4; 81 | if (strncmp(lib, p, strlen(lib))) 82 | continue; 83 | p += strlen(lib); 84 | /* libraries can have - or . after the name */ 85 | if (*p != '.' && *p != '-') 86 | continue; 87 | if (strnlen(path_buf, 1024) >= path_sz) { 88 | warn("path size too small\n"); 89 | return -1; 90 | } 91 | strcpy(path, path_buf); 92 | fclose(maps); 93 | return 0; 94 | } 95 | 96 | warn("Cannot find library %s\n", lib); 97 | fclose(maps); 98 | return -1; 99 | } 100 | 101 | /* 102 | * Returns 0 on success; -1 on failure. On success, returns via `path` the full 103 | * path to the program. 104 | */ 105 | static int which_program(const char *prog, char *path, size_t path_sz) 106 | { 107 | FILE *which; 108 | char cmd[100]; 109 | 110 | if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) { 111 | warn("snprintf which prog failed"); 112 | return -1; 113 | } 114 | which = popen(cmd, "r"); 115 | if (!which) { 116 | warn("which failed"); 117 | return -1; 118 | } 119 | if (!fgets(path, path_sz, which)) { 120 | warn("fgets which failed"); 121 | pclose(which); 122 | return -1; 123 | } 124 | /* which has a \n at the end of the string */ 125 | path[strlen(path) - 1] = '\0'; 126 | pclose(which); 127 | return 0; 128 | } 129 | 130 | /* 131 | * Returns 0 on success; -1 on failure. On success, returns via `path` the full 132 | * path to the binary for the given pid. 133 | * 1) pid == x, binary == "" : returns the path to x's program 134 | * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x 135 | * 3) pid == 0, binary == "" : failure: need a pid or a binary 136 | * 4) pid == 0, binary == "bar" : returns the path to `which bar` 137 | * 138 | * For case 4), ideally we'd like to search for libbar too, but we don't support 139 | * that yet. 140 | */ 141 | int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz) 142 | { 143 | if (!strcmp(binary, "")) { 144 | if (!pid) { 145 | warn("Uprobes need a pid or a binary\n"); 146 | return -1; 147 | } 148 | return get_pid_binary_path(pid, path, path_sz); 149 | } 150 | if (pid) 151 | return get_pid_lib_path(pid, binary, path, path_sz); 152 | 153 | if (which_program(binary, path, path_sz)) { 154 | /* 155 | * If the user is tracing a program by name, we can find it. 156 | * But we can't find a library by name yet. We'd need to parse 157 | * ld.so.cache or something similar. 158 | */ 159 | warn("Can't find %s (Need a PID if this is a library)\n", binary); 160 | return -1; 161 | } 162 | return 0; 163 | } 164 | 165 | /* 166 | * Opens an elf at `path` of kind ELF_K_ELF. Returns NULL on failure. On 167 | * success, close with close_elf(e, fd_close). 168 | */ 169 | Elf *open_elf(const char *path, int *fd_close) 170 | { 171 | int fd; 172 | Elf *e; 173 | 174 | if (elf_version(EV_CURRENT) == EV_NONE) { 175 | warn("elf init failed\n"); 176 | return NULL; 177 | } 178 | fd = open(path, O_RDONLY); 179 | if (fd < 0) { 180 | warn("Could not open %s\n", path); 181 | return NULL; 182 | } 183 | e = elf_begin(fd, ELF_C_READ, NULL); 184 | if (!e) { 185 | warn("elf_begin failed: %s\n", elf_errmsg(-1)); 186 | close(fd); 187 | return NULL; 188 | } 189 | if (elf_kind(e) != ELF_K_ELF) { 190 | warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); 191 | elf_end(e); 192 | close(fd); 193 | return NULL; 194 | } 195 | *fd_close = fd; 196 | return e; 197 | } 198 | 199 | Elf *open_elf_by_fd(int fd) 200 | { 201 | Elf *e; 202 | 203 | if (elf_version(EV_CURRENT) == EV_NONE) { 204 | warn("elf init failed\n"); 205 | return NULL; 206 | } 207 | e = elf_begin(fd, ELF_C_READ, NULL); 208 | if (!e) { 209 | warn("elf_begin failed: %s\n", elf_errmsg(-1)); 210 | close(fd); 211 | return NULL; 212 | } 213 | if (elf_kind(e) != ELF_K_ELF) { 214 | warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); 215 | elf_end(e); 216 | close(fd); 217 | return NULL; 218 | } 219 | return e; 220 | } 221 | 222 | void close_elf(Elf *e, int fd_close) 223 | { 224 | elf_end(e); 225 | close(fd_close); 226 | } 227 | 228 | /* Returns the offset of a function in the elf file `path`, or -1 on failure. */ 229 | off_t get_elf_func_offset(const char *path, const char *func) 230 | { 231 | off_t ret = -1; 232 | int i, fd = -1; 233 | Elf *e; 234 | Elf_Scn *scn; 235 | Elf_Data *data; 236 | GElf_Ehdr ehdr; 237 | GElf_Shdr shdr[1]; 238 | GElf_Phdr phdr; 239 | GElf_Sym sym[1]; 240 | size_t shstrndx, nhdrs; 241 | char *n; 242 | 243 | e = open_elf(path, &fd); 244 | 245 | if (!gelf_getehdr(e, &ehdr)) 246 | goto out; 247 | 248 | if (elf_getshdrstrndx(e, &shstrndx) != 0) 249 | goto out; 250 | 251 | scn = NULL; 252 | while ((scn = elf_nextscn(e, scn))) { 253 | if (!gelf_getshdr(scn, shdr)) 254 | continue; 255 | if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM)) 256 | continue; 257 | data = NULL; 258 | while ((data = elf_getdata(scn, data))) { 259 | for (i = 0; gelf_getsym(data, i, sym); i++) { 260 | n = elf_strptr(e, shdr->sh_link, sym->st_name); 261 | if (!n) 262 | continue; 263 | if (GELF_ST_TYPE(sym->st_info) != STT_FUNC) 264 | continue; 265 | if (!strcmp(n, func)) { 266 | ret = sym->st_value; 267 | goto check; 268 | } 269 | } 270 | } 271 | } 272 | 273 | check: 274 | if (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN) { 275 | if (elf_getphdrnum(e, &nhdrs) != 0) { 276 | ret = -1; 277 | goto out; 278 | } 279 | for (i = 0; i < (int)nhdrs; i++) { 280 | if (!gelf_getphdr(e, i, &phdr)) 281 | continue; 282 | if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X)) 283 | continue; 284 | if (phdr.p_vaddr <= ret && ret < (phdr.p_vaddr + phdr.p_memsz)) { 285 | ret = ret - phdr.p_vaddr + phdr.p_offset; 286 | goto out; 287 | } 288 | } 289 | ret = -1; 290 | } 291 | out: 292 | close_elf(e, fd); 293 | return ret; 294 | } 295 | -------------------------------------------------------------------------------- /src/bpf_dctcp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* Copyright (c) 2019 Facebook */ 3 | 4 | /* WARNING: This implemenation is not necessarily the same 5 | * as the tcp_dctcp.c. The purpose is mainly for testing 6 | * the kernel BPF logic. 7 | */ 8 | 9 | #include "bpf_tracing_net.h" 10 | #include 11 | #include 12 | 13 | #ifndef EBUSY 14 | #define EBUSY 16 15 | #endif 16 | #define min(a, b) ((a) < (b) ? (a) : (b)) 17 | #define max(a, b) ((a) > (b) ? (a) : (b)) 18 | #define min_not_zero(x, y) ({ \ 19 | typeof(x) __x = (x); \ 20 | typeof(y) __y = (y); \ 21 | __x == 0 ? __y : ((__y == 0) ? __x : min(__x, __y)); }) 22 | static bool before(__u32 seq1, __u32 seq2) 23 | { 24 | return (__s32)(seq1-seq2) < 0; 25 | } 26 | 27 | char _license[] SEC("license") = "GPL"; 28 | 29 | volatile const char fallback_cc[TCP_CA_NAME_MAX]; 30 | const char bpf_dctcp[] = "bpf_dctcp"; 31 | const char tcp_cdg[] = "cdg"; 32 | char cc_res[TCP_CA_NAME_MAX]; 33 | int tcp_cdg_res = 0; 34 | int stg_result = 0; 35 | int ebusy_cnt = 0; 36 | 37 | struct { 38 | __uint(type, BPF_MAP_TYPE_SK_STORAGE); 39 | __uint(map_flags, BPF_F_NO_PREALLOC); 40 | __type(key, int); 41 | __type(value, int); 42 | } sk_stg_map SEC(".maps"); 43 | 44 | #define DCTCP_MAX_ALPHA 1024U 45 | 46 | struct bpf_dctcp { 47 | __u32 old_delivered; 48 | __u32 old_delivered_ce; 49 | __u32 prior_rcv_nxt; 50 | __u32 dctcp_alpha; 51 | __u32 next_seq; 52 | __u32 ce_state; 53 | __u32 loss_cwnd; 54 | }; 55 | 56 | static unsigned int dctcp_shift_g = 4; /* g = 1/2^4 */ 57 | static unsigned int dctcp_alpha_on_init = DCTCP_MAX_ALPHA; 58 | 59 | static void dctcp_reset(const struct tcp_sock *tp, struct bpf_dctcp *ca) 60 | { 61 | ca->next_seq = tp->snd_nxt; 62 | 63 | ca->old_delivered = tp->delivered; 64 | ca->old_delivered_ce = tp->delivered_ce; 65 | } 66 | 67 | SEC("struct_ops") 68 | void BPF_PROG(bpf_dctcp_init, struct sock *sk) 69 | { 70 | const struct tcp_sock *tp = tcp_sk(sk); 71 | struct bpf_dctcp *ca = inet_csk_ca(sk); 72 | int *stg; 73 | 74 | if (!(tp->ecn_flags & TCP_ECN_OK) && fallback_cc[0]) { 75 | /* Switch to fallback */ 76 | if (bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 77 | (void *)fallback_cc, sizeof(fallback_cc)) == -EBUSY) 78 | ebusy_cnt++; 79 | 80 | /* Switch back to myself and the recurred bpf_dctcp_init() 81 | * will get -EBUSY for all bpf_setsockopt(TCP_CONGESTION), 82 | * except the last "cdg" one. 83 | */ 84 | if (bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 85 | (void *)bpf_dctcp, sizeof(bpf_dctcp)) == -EBUSY) 86 | ebusy_cnt++; 87 | 88 | /* Switch back to fallback */ 89 | if (bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 90 | (void *)fallback_cc, sizeof(fallback_cc)) == -EBUSY) 91 | ebusy_cnt++; 92 | 93 | /* Expecting -ENOTSUPP for tcp_cdg_res */ 94 | tcp_cdg_res = bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 95 | (void *)tcp_cdg, sizeof(tcp_cdg)); 96 | bpf_getsockopt(sk, SOL_TCP, TCP_CONGESTION, 97 | (void *)cc_res, sizeof(cc_res)); 98 | return; 99 | } 100 | 101 | ca->prior_rcv_nxt = tp->rcv_nxt; 102 | ca->dctcp_alpha = min(dctcp_alpha_on_init, DCTCP_MAX_ALPHA); 103 | ca->loss_cwnd = 0; 104 | ca->ce_state = 0; 105 | 106 | stg = bpf_sk_storage_get(&sk_stg_map, (void *)tp, NULL, 0); 107 | if (stg) { 108 | stg_result = *stg; 109 | bpf_sk_storage_delete(&sk_stg_map, (void *)tp); 110 | } 111 | dctcp_reset(tp, ca); 112 | } 113 | 114 | SEC("struct_ops") 115 | __u32 BPF_PROG(bpf_dctcp_ssthresh, struct sock *sk) 116 | { 117 | struct bpf_dctcp *ca = inet_csk_ca(sk); 118 | struct tcp_sock *tp = tcp_sk(sk); 119 | 120 | ca->loss_cwnd = tp->snd_cwnd; 121 | return max(tp->snd_cwnd - ((tp->snd_cwnd * ca->dctcp_alpha) >> 11U), 2U); 122 | } 123 | 124 | SEC("struct_ops") 125 | void BPF_PROG(bpf_dctcp_update_alpha, struct sock *sk, __u32 flags) 126 | { 127 | const struct tcp_sock *tp = tcp_sk(sk); 128 | struct bpf_dctcp *ca = inet_csk_ca(sk); 129 | 130 | /* Expired RTT */ 131 | if (!before(tp->snd_una, ca->next_seq)) { 132 | __u32 delivered_ce = tp->delivered_ce - ca->old_delivered_ce; 133 | __u32 alpha = ca->dctcp_alpha; 134 | 135 | /* alpha = (1 - g) * alpha + g * F */ 136 | 137 | alpha -= min_not_zero(alpha, alpha >> dctcp_shift_g); 138 | if (delivered_ce) { 139 | __u32 delivered = tp->delivered - ca->old_delivered; 140 | 141 | /* If dctcp_shift_g == 1, a 32bit value would overflow 142 | * after 8 M packets. 143 | */ 144 | delivered_ce <<= (10 - dctcp_shift_g); 145 | delivered_ce /= max(1U, delivered); 146 | 147 | alpha = min(alpha + delivered_ce, DCTCP_MAX_ALPHA); 148 | } 149 | ca->dctcp_alpha = alpha; 150 | dctcp_reset(tp, ca); 151 | } 152 | } 153 | 154 | static void dctcp_react_to_loss(struct sock *sk) 155 | { 156 | struct bpf_dctcp *ca = inet_csk_ca(sk); 157 | struct tcp_sock *tp = tcp_sk(sk); 158 | 159 | ca->loss_cwnd = tp->snd_cwnd; 160 | tp->snd_ssthresh = max(tp->snd_cwnd >> 1U, 2U); 161 | } 162 | 163 | SEC("struct_ops") 164 | void BPF_PROG(bpf_dctcp_state, struct sock *sk, __u8 new_state) 165 | { 166 | if (new_state == TCP_CA_Recovery && 167 | new_state != BPF_CORE_READ_BITFIELD(inet_csk(sk), icsk_ca_state)) 168 | dctcp_react_to_loss(sk); 169 | /* We handle RTO in bpf_dctcp_cwnd_event to ensure that we perform only 170 | * one loss-adjustment per RTT. 171 | */ 172 | } 173 | 174 | static void dctcp_ece_ack_cwr(struct sock *sk, __u32 ce_state) 175 | { 176 | struct tcp_sock *tp = tcp_sk(sk); 177 | 178 | if (ce_state == 1) 179 | tp->ecn_flags |= TCP_ECN_DEMAND_CWR; 180 | else 181 | tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; 182 | } 183 | 184 | /* Minimal DCTP CE state machine: 185 | * 186 | * S: 0 <- last pkt was non-CE 187 | * 1 <- last pkt was CE 188 | */ 189 | static void dctcp_ece_ack_update(struct sock *sk, enum tcp_ca_event evt, 190 | __u32 *prior_rcv_nxt, __u32 *ce_state) 191 | { 192 | __u32 new_ce_state = (evt == CA_EVENT_ECN_IS_CE) ? 1 : 0; 193 | 194 | if (*ce_state != new_ce_state) { 195 | /* CE state has changed, force an immediate ACK to 196 | * reflect the new CE state. If an ACK was delayed, 197 | * send that first to reflect the prior CE state. 198 | */ 199 | if (inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER) { 200 | dctcp_ece_ack_cwr(sk, *ce_state); 201 | bpf_tcp_send_ack(sk, *prior_rcv_nxt); 202 | } 203 | inet_csk(sk)->icsk_ack.pending |= ICSK_ACK_NOW; 204 | } 205 | *prior_rcv_nxt = tcp_sk(sk)->rcv_nxt; 206 | *ce_state = new_ce_state; 207 | dctcp_ece_ack_cwr(sk, new_ce_state); 208 | } 209 | 210 | SEC("struct_ops") 211 | void BPF_PROG(bpf_dctcp_cwnd_event, struct sock *sk, enum tcp_ca_event ev) 212 | { 213 | struct bpf_dctcp *ca = inet_csk_ca(sk); 214 | 215 | switch (ev) { 216 | case CA_EVENT_ECN_IS_CE: 217 | case CA_EVENT_ECN_NO_CE: 218 | dctcp_ece_ack_update(sk, ev, &ca->prior_rcv_nxt, &ca->ce_state); 219 | break; 220 | case CA_EVENT_LOSS: 221 | dctcp_react_to_loss(sk); 222 | break; 223 | default: 224 | /* Don't care for the rest. */ 225 | break; 226 | } 227 | } 228 | 229 | SEC("struct_ops") 230 | __u32 BPF_PROG(bpf_dctcp_cwnd_undo, struct sock *sk) 231 | { 232 | const struct bpf_dctcp *ca = inet_csk_ca(sk); 233 | 234 | return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); 235 | } 236 | 237 | extern void tcp_reno_cong_avoid(struct sock *sk, __u32 ack, __u32 acked) __ksym; 238 | 239 | SEC("struct_ops") 240 | void BPF_PROG(bpf_dctcp_cong_avoid, struct sock *sk, __u32 ack, __u32 acked) 241 | { 242 | tcp_reno_cong_avoid(sk, ack, acked); 243 | } 244 | 245 | SEC(".struct_ops") 246 | struct tcp_congestion_ops dctcp_nouse = { 247 | .init = (void *)bpf_dctcp_init, 248 | .set_state = (void *)bpf_dctcp_state, 249 | .flags = TCP_CONG_NEEDS_ECN, 250 | .name = "bpf_dctcp_nouse", 251 | }; 252 | 253 | SEC(".struct_ops") 254 | struct tcp_congestion_ops dctcp = { 255 | .init = (void *)bpf_dctcp_init, 256 | .in_ack_event = (void *)bpf_dctcp_update_alpha, 257 | .cwnd_event = (void *)bpf_dctcp_cwnd_event, 258 | .ssthresh = (void *)bpf_dctcp_ssthresh, 259 | .cong_avoid = (void *)bpf_dctcp_cong_avoid, 260 | .undo_cwnd = (void *)bpf_dctcp_cwnd_undo, 261 | .set_state = (void *)bpf_dctcp_state, 262 | .flags = TCP_CONG_NEEDS_ECN, 263 | .name = "bpf_dctcp", 264 | }; 265 | -------------------------------------------------------------------------------- /src/profile.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "profile.skel.h" 17 | #include "profile.h" 18 | #include "blazesym.h" 19 | 20 | /* 21 | * This function is from libbpf, but it is not a public API and can only be 22 | * used for demonstration. We can use this here because we statically link 23 | * against the libbpf built from submodule during build. 24 | */ 25 | extern int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); 26 | 27 | static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, 28 | unsigned long flags) 29 | { 30 | int ret; 31 | 32 | ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); 33 | return ret; 34 | } 35 | 36 | static struct blaze_symbolizer *symbolizer; 37 | 38 | static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info* code_info) 39 | { 40 | // If we have an input address we have a new symbol. 41 | if (input_addr != 0) { 42 | printf("%016lx: %s @ 0x%lx+0x%lx", input_addr, name, addr, offset); 43 | if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { 44 | printf(" %s/%s:%u\n", code_info->dir, code_info->file, code_info->line); 45 | } else if (code_info != NULL && code_info->file != NULL) { 46 | printf(" %s:%u\n", code_info->file, code_info->line); 47 | } else { 48 | printf("\n"); 49 | } 50 | } else { 51 | printf("%16s %s", "", name); 52 | if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { 53 | printf("@ %s/%s:%u [inlined]\n", code_info->dir, code_info->file, code_info->line); 54 | } else if (code_info != NULL && code_info->file != NULL) { 55 | printf("@ %s:%u [inlined]\n", code_info->file, code_info->line); 56 | } else { 57 | printf("[inlined]\n"); 58 | } 59 | } 60 | } 61 | 62 | static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) 63 | { 64 | const struct blaze_symbolize_inlined_fn* inlined; 65 | const struct blaze_syms *syms; 66 | const struct blaze_sym *sym; 67 | int i, j; 68 | 69 | assert(sizeof(uintptr_t) == sizeof(uint64_t)); 70 | 71 | if (pid) { 72 | struct blaze_symbolize_src_process src = { 73 | .type_size = sizeof(src), 74 | .pid = pid, 75 | }; 76 | syms = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); 77 | } else { 78 | struct blaze_symbolize_src_kernel src = { 79 | .type_size = sizeof(src), 80 | }; 81 | syms = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); 82 | } 83 | 84 | if (syms == NULL) { 85 | printf(" failed to symbolize addresses: %s\n", blaze_err_str(blaze_err_last())); 86 | return; 87 | } 88 | 89 | for (i = 0; i < stack_sz; i++) { 90 | if (!syms || syms->cnt <= i || syms->syms[i].name == NULL) { 91 | printf("%016llx: \n", stack[i]); 92 | continue; 93 | } 94 | 95 | sym = &syms->syms[i]; 96 | print_frame(sym->name, stack[i], sym->addr, sym->offset, &sym->code_info); 97 | 98 | for (j = 0; j < sym->inlined_cnt; j++) { 99 | inlined = &sym->inlined[j]; 100 | print_frame(sym->name, 0, 0, 0, &inlined->code_info); 101 | } 102 | } 103 | 104 | blaze_syms_free(syms); 105 | } 106 | 107 | /* Receive events from the ring buffer. */ 108 | static int event_handler(void *_ctx, void *data, size_t size) 109 | { 110 | struct stacktrace_event *event = data; 111 | 112 | if (event->kstack_sz <= 0 && event->ustack_sz <= 0) 113 | return 1; 114 | 115 | printf("COMM: %s (pid=%d) @ CPU %d\n", event->comm, event->pid, event->cpu_id); 116 | 117 | if (event->kstack_sz > 0) { 118 | printf("Kernel:\n"); 119 | show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0); 120 | } else { 121 | printf("No Kernel Stack\n"); 122 | } 123 | 124 | if (event->ustack_sz > 0) { 125 | printf("Userspace:\n"); 126 | show_stack_trace(event->ustack, event->ustack_sz / sizeof(__u64), event->pid); 127 | } else { 128 | printf("No Userspace Stack\n"); 129 | } 130 | 131 | printf("\n"); 132 | return 0; 133 | } 134 | 135 | static void show_help(const char *progname) 136 | { 137 | printf("Usage: %s [-f ] [--sw-event] [-h]\n", progname); 138 | printf("Options:\n"); 139 | printf(" -f Sampling frequency [default: 1]\n"); 140 | printf(" --sw-event Use software event for triggering stack trace capture\n"); 141 | printf(" -h Print help\n"); 142 | } 143 | 144 | int main(int argc, char *const argv[]) 145 | { 146 | const char *online_cpus_file = "/sys/devices/system/cpu/online"; 147 | int freq = 1, sw_event = 0, pid = -1, cpu; 148 | struct profile_bpf *skel = NULL; 149 | struct perf_event_attr attr; 150 | struct bpf_link **links = NULL; 151 | struct ring_buffer *ring_buf = NULL; 152 | int num_cpus, num_online_cpus; 153 | int *pefds = NULL, pefd; 154 | int argp, i, err = 0; 155 | bool *online_mask = NULL; 156 | 157 | static struct option long_options[] = { 158 | {"sw-event", no_argument, 0, 's'}, 159 | {0, 0, 0, 0} 160 | }; 161 | 162 | while ((argp = getopt_long(argc, argv, "hf:", long_options, NULL)) != -1) { 163 | switch (argp) { 164 | case 'f': 165 | freq = atoi(optarg); 166 | if (freq < 1) 167 | freq = 1; 168 | break; 169 | case 's': 170 | sw_event = 1; 171 | break; 172 | 173 | case 'h': 174 | default: 175 | show_help(argv[0]); 176 | return 1; 177 | } 178 | } 179 | 180 | err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); 181 | if (err) { 182 | fprintf(stderr, "Fail to get online CPU numbers: %d\n", err); 183 | goto cleanup; 184 | } 185 | 186 | num_cpus = libbpf_num_possible_cpus(); 187 | if (num_cpus <= 0) { 188 | fprintf(stderr, "Fail to get the number of processors\n"); 189 | err = -1; 190 | goto cleanup; 191 | } 192 | 193 | skel = profile_bpf__open_and_load(); 194 | if (!skel) { 195 | fprintf(stderr, "Fail to open and load BPF skeleton\n"); 196 | err = -1; 197 | goto cleanup; 198 | } 199 | 200 | symbolizer = blaze_symbolizer_new(); 201 | if (!symbolizer) { 202 | fprintf(stderr, "Fail to create a symbolizer\n"); 203 | err = -1; 204 | goto cleanup; 205 | } 206 | 207 | /* Prepare ring buffer to receive events from the BPF program. */ 208 | ring_buf = ring_buffer__new(bpf_map__fd(skel->maps.events), event_handler, NULL, NULL); 209 | if (!ring_buf) { 210 | err = -1; 211 | goto cleanup; 212 | } 213 | 214 | pefds = malloc(num_cpus * sizeof(int)); 215 | for (i = 0; i < num_cpus; i++) { 216 | pefds[i] = -1; 217 | } 218 | 219 | links = calloc(num_cpus, sizeof(struct bpf_link *)); 220 | 221 | memset(&attr, 0, sizeof(attr)); 222 | attr.type = sw_event ? PERF_TYPE_SOFTWARE : PERF_TYPE_HARDWARE; 223 | attr.size = sizeof(attr); 224 | attr.config = sw_event ? PERF_COUNT_SW_CPU_CLOCK : PERF_COUNT_HW_CPU_CYCLES; 225 | attr.sample_freq = freq; 226 | attr.freq = 1; 227 | 228 | for (cpu = 0; cpu < num_cpus; cpu++) { 229 | /* skip offline/not present CPUs */ 230 | if (cpu >= num_online_cpus || !online_mask[cpu]) 231 | continue; 232 | 233 | /* Set up performance monitoring on a CPU/Core */ 234 | pefd = perf_event_open(&attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); 235 | if (pefd < 0) { 236 | if (!sw_event && errno == ENOENT) { 237 | fprintf(stderr, 238 | "Fail to set up performance monitor on a CPU/Core.\n" 239 | "Try running the profile example with the `--sw-event` option.\n"); 240 | } else { 241 | fprintf(stderr, "Fail to set up performance monitor on a CPU/Core.\n"); 242 | } 243 | err = -1; 244 | goto cleanup; 245 | } 246 | pefds[cpu] = pefd; 247 | 248 | /* Attach a BPF program on a CPU */ 249 | links[cpu] = bpf_program__attach_perf_event(skel->progs.profile, pefd); 250 | if (!links[cpu]) { 251 | err = -1; 252 | goto cleanup; 253 | } 254 | } 255 | 256 | /* Wait and receive stack traces */ 257 | while (ring_buffer__poll(ring_buf, -1) >= 0) { 258 | } 259 | 260 | cleanup: 261 | if (links) { 262 | for (cpu = 0; cpu < num_cpus; cpu++) 263 | bpf_link__destroy(links[cpu]); 264 | free(links); 265 | } 266 | if (pefds) { 267 | for (i = 0; i < num_cpus; i++) { 268 | if (pefds[i] >= 0) 269 | close(pefds[i]); 270 | } 271 | free(pefds); 272 | } 273 | ring_buffer__free(ring_buf); 274 | profile_bpf__destroy(skel); 275 | blaze_symbolizer_free(symbolizer); 276 | free(online_mask); 277 | return -err; 278 | } 279 | -------------------------------------------------------------------------------- /src/runqlen.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Wenbo Zhang 3 | // 4 | // Based on runqlen(8) from BCC by Brendan Gregg. 5 | // 11-Sep-2020 Wenbo Zhang Created this. 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "runqlen.h" 18 | #include "runqlen.skel.h" 19 | #include "btf_helpers.h" 20 | #include "trace_helpers.h" 21 | 22 | #define max(x, y) ({ \ 23 | typeof(x) _max1 = (x); \ 24 | typeof(y) _max2 = (y); \ 25 | (void) (&_max1 == &_max2); \ 26 | _max1 > _max2 ? _max1 : _max2; }) 27 | 28 | struct env { 29 | bool per_cpu; 30 | bool runqocc; 31 | bool timestamp; 32 | bool host; 33 | time_t interval; 34 | int freq; 35 | int times; 36 | bool verbose; 37 | } env = { 38 | .interval = 99999999, 39 | .times = 99999999, 40 | .freq = 99, 41 | }; 42 | 43 | static volatile bool exiting; 44 | 45 | const char *argp_program_version = "runqlen 0.1"; 46 | const char *argp_program_bug_address = 47 | "https://github.com/iovisor/bcc/tree/master/libbpf-tools"; 48 | const char argp_program_doc[] = 49 | "Summarize scheduler run queue length as a histogram.\n" 50 | "\n" 51 | "USAGE: runqlen [--help] [-C] [-O] [-T] [-f FREQUENCY] [interval] [count]\n" 52 | "\n" 53 | "EXAMPLES:\n" 54 | " runqlen # summarize run queue length as a histogram\n" 55 | " runqlen 1 10 # print 1 second summaries, 10 times\n" 56 | " runqlen -T 1 # 1s summaries and timestamps\n" 57 | " runqlen -O # report run queue occupancy\n" 58 | " runqlen -C # show each CPU separately\n" 59 | " runqlen -H # show nr_running from host's rq instead of cfs_rq\n" 60 | " runqlen -f 199 # sample at 199HZ\n"; 61 | 62 | static const struct argp_option opts[] = { 63 | { "cpus", 'C', NULL, 0, "Print output for each CPU separately", 0 }, 64 | { "frequency", 'f', "FREQUENCY", 0, "Sample with a certain frequency", 0 }, 65 | { "runqocc", 'O', NULL, 0, "Report run queue occupancy", 0 }, 66 | { "timestamp", 'T', NULL, 0, "Include timestamp on output", 0 }, 67 | { "verbose", 'v', NULL, 0, "Verbose debug output", 0 }, 68 | { "host", 'H', NULL, 0, "Report nr_running from host's rq", 0 }, 69 | { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help", 0 }, 70 | {}, 71 | }; 72 | 73 | static error_t parse_arg(int key, char *arg, struct argp_state *state) 74 | { 75 | static int pos_args; 76 | 77 | switch (key) { 78 | case 'h': 79 | argp_state_help(state, stderr, ARGP_HELP_STD_HELP); 80 | break; 81 | case 'v': 82 | env.verbose = true; 83 | break; 84 | case 'C': 85 | env.per_cpu = true; 86 | break; 87 | case 'O': 88 | env.runqocc = true; 89 | break; 90 | case 'T': 91 | env.timestamp = true; 92 | break; 93 | case 'H': 94 | env.host = true; 95 | break; 96 | case 'f': 97 | errno = 0; 98 | env.freq = strtol(arg, NULL, 10); 99 | if (errno || env.freq <= 0) { 100 | fprintf(stderr, "Invalid freq (in hz): %s\n", arg); 101 | argp_usage(state); 102 | } 103 | break; 104 | case ARGP_KEY_ARG: 105 | errno = 0; 106 | if (pos_args == 0) { 107 | env.interval = strtol(arg, NULL, 10); 108 | if (errno) { 109 | fprintf(stderr, "invalid internal\n"); 110 | argp_usage(state); 111 | } 112 | } else if (pos_args == 1) { 113 | env.times = strtol(arg, NULL, 10); 114 | if (errno) { 115 | fprintf(stderr, "invalid times\n"); 116 | argp_usage(state); 117 | } 118 | } else { 119 | fprintf(stderr, 120 | "unrecognized positional argument: %s\n", arg); 121 | argp_usage(state); 122 | } 123 | pos_args++; 124 | break; 125 | default: 126 | return ARGP_ERR_UNKNOWN; 127 | } 128 | return 0; 129 | } 130 | 131 | static int nr_cpus; 132 | 133 | static int open_and_attach_perf_event(int freq, struct bpf_program *prog, 134 | struct bpf_link *links[]) 135 | { 136 | struct perf_event_attr attr = { 137 | .type = PERF_TYPE_SOFTWARE, 138 | .freq = 1, 139 | .sample_period = freq, 140 | .config = PERF_COUNT_SW_CPU_CLOCK, 141 | }; 142 | int i, fd; 143 | 144 | for (i = 0; i < nr_cpus; i++) { 145 | fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0); 146 | if (fd < 0) { 147 | /* Ignore CPU that is offline */ 148 | if (errno == ENODEV) 149 | continue; 150 | fprintf(stderr, "failed to init perf sampling: %s\n", 151 | strerror(errno)); 152 | return -1; 153 | } 154 | links[i] = bpf_program__attach_perf_event(prog, fd); 155 | if (!links[i]) { 156 | fprintf(stderr, "failed to attach perf event on cpu: %d\n", i); 157 | close(fd); 158 | return -1; 159 | } 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 166 | { 167 | if (level == LIBBPF_DEBUG && !env.verbose) 168 | return 0; 169 | return vfprintf(stderr, format, args); 170 | } 171 | 172 | static void sig_handler(int sig) 173 | { 174 | exiting = true; 175 | } 176 | 177 | static struct hist zero; 178 | 179 | static void print_runq_occupancy(struct runqlen_bpf__bss *bss) 180 | { 181 | struct hist hist; 182 | int slot, i = 0; 183 | float runqocc; 184 | 185 | do { 186 | __u64 samples, idle = 0, queued = 0; 187 | 188 | hist = bss->hists[i]; 189 | bss->hists[i] = zero; 190 | for (slot = 0; slot < MAX_SLOTS; slot++) { 191 | __u64 val = hist.slots[slot]; 192 | 193 | if (slot == 0) 194 | idle += val; 195 | else 196 | queued += val; 197 | } 198 | samples = idle + queued; 199 | runqocc = queued * 1.0 / max(1ULL, samples); 200 | if (env.per_cpu) 201 | printf("runqocc, CPU %-3d %6.2f%%\n", i, 202 | 100 * runqocc); 203 | else 204 | printf("runqocc: %0.2f%%\n", 100 * runqocc); 205 | } while (env.per_cpu && ++i < nr_cpus); 206 | } 207 | 208 | static void print_linear_hists(struct runqlen_bpf__bss *bss) 209 | { 210 | struct hist hist; 211 | int i = 0; 212 | 213 | do { 214 | hist = bss->hists[i]; 215 | bss->hists[i] = zero; 216 | if (env.per_cpu) 217 | printf("cpu = %d\n", i); 218 | print_linear_hist(hist.slots, MAX_SLOTS, 0, 1, "runqlen"); 219 | } while (env.per_cpu && ++i < nr_cpus); 220 | } 221 | 222 | int main(int argc, char **argv) 223 | { 224 | LIBBPF_OPTS(bpf_object_open_opts, open_opts); 225 | static const struct argp argp = { 226 | .options = opts, 227 | .parser = parse_arg, 228 | .doc = argp_program_doc, 229 | }; 230 | struct bpf_link *links[MAX_CPU_NR] = {}; 231 | struct runqlen_bpf *obj; 232 | struct tm *tm; 233 | char ts[32]; 234 | int err, i; 235 | time_t t; 236 | 237 | err = argp_parse(&argp, argc, argv, 0, NULL, NULL); 238 | if (err) 239 | return err; 240 | 241 | libbpf_set_print(libbpf_print_fn); 242 | 243 | nr_cpus = libbpf_num_possible_cpus(); 244 | if (nr_cpus < 0) { 245 | printf("failed to get # of possible cpus: '%s'!\n", 246 | strerror(-nr_cpus)); 247 | return 1; 248 | } 249 | if (nr_cpus > MAX_CPU_NR) { 250 | fprintf(stderr, "the number of cpu cores is too big, please " 251 | "increase MAX_CPU_NR's value and recompile"); 252 | return 1; 253 | } 254 | 255 | err = ensure_core_btf(&open_opts); 256 | if (err) { 257 | fprintf(stderr, "failed to fetch necessary BTF for CO-RE: %s\n", strerror(-err)); 258 | return 1; 259 | } 260 | 261 | obj = runqlen_bpf__open_opts(&open_opts); 262 | if (!obj) { 263 | fprintf(stderr, "failed to open BPF object\n"); 264 | return 1; 265 | } 266 | 267 | /* initialize global data (filtering options) */ 268 | obj->rodata->targ_per_cpu = env.per_cpu; 269 | obj->rodata->targ_host = env.host; 270 | 271 | err = runqlen_bpf__load(obj); 272 | if (err) { 273 | fprintf(stderr, "failed to load BPF object: %d\n", err); 274 | goto cleanup; 275 | } 276 | 277 | if (!obj->bss) { 278 | fprintf(stderr, "Memory-mapping BPF maps is supported starting from Linux 5.7, please upgrade.\n"); 279 | goto cleanup; 280 | } 281 | 282 | err = open_and_attach_perf_event(env.freq, obj->progs.do_sample, links); 283 | if (err) 284 | goto cleanup; 285 | 286 | printf("Sampling run queue length... Hit Ctrl-C to end.\n"); 287 | 288 | signal(SIGINT, sig_handler); 289 | 290 | while (1) { 291 | sleep(env.interval); 292 | printf("\n"); 293 | 294 | if (env.timestamp) { 295 | time(&t); 296 | tm = localtime(&t); 297 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 298 | printf("%-8s\n", ts); 299 | } 300 | 301 | if (env.runqocc) 302 | print_runq_occupancy(obj->bss); 303 | else 304 | print_linear_hists(obj->bss); 305 | 306 | if (exiting || --env.times == 0) 307 | break; 308 | } 309 | 310 | cleanup: 311 | for (i = 0; i < nr_cpus; i++) 312 | bpf_link__destroy(links[i]); 313 | runqlen_bpf__destroy(obj); 314 | cleanup_core_btf(&open_opts); 315 | 316 | return err != 0; 317 | } 318 | -------------------------------------------------------------------------------- /src/cgroup_link.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | #include 4 | #include "cgroup_helpers.h" 5 | #include "testing_helpers.h" 6 | #include "test_cgroup_link.skel.h" 7 | 8 | static __u32 duration = 0; 9 | #define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null" 10 | 11 | static struct test_cgroup_link *skel = NULL; 12 | 13 | int ping_and_check(int exp_calls, int exp_alt_calls) 14 | { 15 | skel->bss->calls = 0; 16 | skel->bss->alt_calls = 0; 17 | CHECK_FAIL(system(PING_CMD)); 18 | if (CHECK(skel->bss->calls != exp_calls, "call_cnt", 19 | "exp %d, got %d\n", exp_calls, skel->bss->calls)) 20 | return -EINVAL; 21 | if (CHECK(skel->bss->alt_calls != exp_alt_calls, "alt_call_cnt", 22 | "exp %d, got %d\n", exp_alt_calls, skel->bss->alt_calls)) 23 | return -EINVAL; 24 | return 0; 25 | } 26 | 27 | void serial_test_cgroup_link(void) 28 | { 29 | struct { 30 | const char *path; 31 | int fd; 32 | } cgs[] = { 33 | { "/cg1" }, 34 | { "/cg1/cg2" }, 35 | { "/cg1/cg2/cg3" }, 36 | { "/cg1/cg2/cg3/cg4" }, 37 | }; 38 | int last_cg = ARRAY_SIZE(cgs) - 1, cg_nr = ARRAY_SIZE(cgs); 39 | DECLARE_LIBBPF_OPTS(bpf_link_update_opts, link_upd_opts); 40 | struct bpf_link *links[ARRAY_SIZE(cgs)] = {}, *tmp_link; 41 | __u32 prog_ids[ARRAY_SIZE(cgs)], prog_cnt = 0, attach_flags, prog_id; 42 | struct bpf_link_info info; 43 | int i = 0, err, prog_fd; 44 | bool detach_legacy = false; 45 | 46 | skel = test_cgroup_link__open_and_load(); 47 | if (CHECK(!skel, "skel_open_load", "failed to open/load skeleton\n")) 48 | return; 49 | prog_fd = bpf_program__fd(skel->progs.egress); 50 | 51 | err = setup_cgroup_environment(); 52 | if (CHECK(err, "cg_init", "failed: %d\n", err)) 53 | goto cleanup; 54 | 55 | for (i = 0; i < cg_nr; i++) { 56 | cgs[i].fd = create_and_get_cgroup(cgs[i].path); 57 | if (!ASSERT_GE(cgs[i].fd, 0, "cg_create")) 58 | goto cleanup; 59 | } 60 | 61 | err = join_cgroup(cgs[last_cg].path); 62 | if (CHECK(err, "cg_join", "fail: %d\n", err)) 63 | goto cleanup; 64 | 65 | for (i = 0; i < cg_nr; i++) { 66 | links[i] = bpf_program__attach_cgroup(skel->progs.egress, 67 | cgs[i].fd); 68 | if (!ASSERT_OK_PTR(links[i], "cg_attach")) 69 | goto cleanup; 70 | } 71 | 72 | ping_and_check(cg_nr, 0); 73 | 74 | /* query the number of effective progs and attach flags in root cg */ 75 | err = bpf_prog_query(cgs[0].fd, BPF_CGROUP_INET_EGRESS, 76 | BPF_F_QUERY_EFFECTIVE, &attach_flags, NULL, 77 | &prog_cnt); 78 | CHECK_FAIL(err); 79 | CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI); 80 | if (CHECK(prog_cnt != 1, "effect_cnt", "exp %d, got %d\n", 1, prog_cnt)) 81 | goto cleanup; 82 | 83 | /* query the number of effective progs in last cg */ 84 | err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS, 85 | BPF_F_QUERY_EFFECTIVE, NULL, NULL, 86 | &prog_cnt); 87 | CHECK_FAIL(err); 88 | CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI); 89 | if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n", 90 | cg_nr, prog_cnt)) 91 | goto cleanup; 92 | 93 | /* query the effective prog IDs in last cg */ 94 | err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS, 95 | BPF_F_QUERY_EFFECTIVE, &attach_flags, 96 | prog_ids, &prog_cnt); 97 | CHECK_FAIL(err); 98 | CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI); 99 | if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n", 100 | cg_nr, prog_cnt)) 101 | goto cleanup; 102 | for (i = 1; i < prog_cnt; i++) { 103 | CHECK(prog_ids[i - 1] != prog_ids[i], "prog_id_check", 104 | "idx %d, prev id %d, cur id %d\n", 105 | i, prog_ids[i - 1], prog_ids[i]); 106 | } 107 | 108 | /* detach bottom program and ping again */ 109 | bpf_link__destroy(links[last_cg]); 110 | links[last_cg] = NULL; 111 | 112 | ping_and_check(cg_nr - 1, 0); 113 | 114 | /* mix in with non link-based multi-attachments */ 115 | err = bpf_prog_attach(prog_fd, cgs[last_cg].fd, 116 | BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI); 117 | if (CHECK(err, "cg_attach_legacy", "errno=%d\n", errno)) 118 | goto cleanup; 119 | detach_legacy = true; 120 | 121 | links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress, 122 | cgs[last_cg].fd); 123 | if (!ASSERT_OK_PTR(links[last_cg], "cg_attach")) 124 | goto cleanup; 125 | 126 | ping_and_check(cg_nr + 1, 0); 127 | 128 | /* detach link */ 129 | bpf_link__destroy(links[last_cg]); 130 | links[last_cg] = NULL; 131 | 132 | /* detach legacy */ 133 | err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS); 134 | if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno)) 135 | goto cleanup; 136 | detach_legacy = false; 137 | 138 | /* attach legacy exclusive prog attachment */ 139 | err = bpf_prog_attach(prog_fd, cgs[last_cg].fd, 140 | BPF_CGROUP_INET_EGRESS, 0); 141 | if (CHECK(err, "cg_attach_exclusive", "errno=%d\n", errno)) 142 | goto cleanup; 143 | detach_legacy = true; 144 | 145 | /* attempt to mix in with multi-attach bpf_link */ 146 | tmp_link = bpf_program__attach_cgroup(skel->progs.egress, 147 | cgs[last_cg].fd); 148 | if (!ASSERT_ERR_PTR(tmp_link, "cg_attach_fail")) { 149 | bpf_link__destroy(tmp_link); 150 | goto cleanup; 151 | } 152 | 153 | ping_and_check(cg_nr, 0); 154 | 155 | /* detach */ 156 | err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS); 157 | if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno)) 158 | goto cleanup; 159 | detach_legacy = false; 160 | 161 | ping_and_check(cg_nr - 1, 0); 162 | 163 | /* attach back link-based one */ 164 | links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress, 165 | cgs[last_cg].fd); 166 | if (!ASSERT_OK_PTR(links[last_cg], "cg_attach")) 167 | goto cleanup; 168 | 169 | ping_and_check(cg_nr, 0); 170 | 171 | /* check legacy exclusive prog can't be attached */ 172 | err = bpf_prog_attach(prog_fd, cgs[last_cg].fd, 173 | BPF_CGROUP_INET_EGRESS, 0); 174 | if (CHECK(!err, "cg_attach_exclusive", "unexpected success")) { 175 | bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS); 176 | goto cleanup; 177 | } 178 | 179 | /* replace BPF programs inside their links for all but first link */ 180 | for (i = 1; i < cg_nr; i++) { 181 | err = bpf_link__update_program(links[i], skel->progs.egress_alt); 182 | if (CHECK(err, "prog_upd", "link #%d\n", i)) 183 | goto cleanup; 184 | } 185 | 186 | ping_and_check(1, cg_nr - 1); 187 | 188 | /* Attempt program update with wrong expected BPF program */ 189 | link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress_alt); 190 | link_upd_opts.flags = BPF_F_REPLACE; 191 | err = bpf_link_update(bpf_link__fd(links[0]), 192 | bpf_program__fd(skel->progs.egress_alt), 193 | &link_upd_opts); 194 | if (CHECK(err == 0 || errno != EPERM, "prog_cmpxchg1", 195 | "unexpectedly succeeded, err %d, errno %d\n", err, -errno)) 196 | goto cleanup; 197 | 198 | /* Compare-exchange single link program from egress to egress_alt */ 199 | link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress); 200 | link_upd_opts.flags = BPF_F_REPLACE; 201 | err = bpf_link_update(bpf_link__fd(links[0]), 202 | bpf_program__fd(skel->progs.egress_alt), 203 | &link_upd_opts); 204 | if (CHECK(err, "prog_cmpxchg2", "errno %d\n", -errno)) 205 | goto cleanup; 206 | 207 | /* ping */ 208 | ping_and_check(0, cg_nr); 209 | 210 | /* close cgroup FDs before detaching links */ 211 | for (i = 0; i < cg_nr; i++) { 212 | if (cgs[i].fd > 0) { 213 | close(cgs[i].fd); 214 | cgs[i].fd = -1; 215 | } 216 | } 217 | 218 | /* BPF programs should still get called */ 219 | ping_and_check(0, cg_nr); 220 | 221 | prog_id = link_info_prog_id(links[0], &info); 222 | CHECK(prog_id == 0, "link_info", "failed\n"); 223 | CHECK(info.cgroup.cgroup_id == 0, "cgroup_id", "unexpected %llu\n", info.cgroup.cgroup_id); 224 | 225 | err = bpf_link__detach(links[0]); 226 | if (CHECK(err, "link_detach", "failed %d\n", err)) 227 | goto cleanup; 228 | 229 | /* cgroup_id should be zero in link_info */ 230 | prog_id = link_info_prog_id(links[0], &info); 231 | CHECK(prog_id == 0, "link_info", "failed\n"); 232 | CHECK(info.cgroup.cgroup_id != 0, "cgroup_id", "unexpected %llu\n", info.cgroup.cgroup_id); 233 | 234 | /* First BPF program shouldn't be called anymore */ 235 | ping_and_check(0, cg_nr - 1); 236 | 237 | /* leave cgroup and remove them, don't detach programs */ 238 | cleanup_cgroup_environment(); 239 | 240 | /* BPF programs should have been auto-detached */ 241 | ping_and_check(0, 0); 242 | 243 | cleanup: 244 | if (detach_legacy) 245 | bpf_prog_detach2(prog_fd, cgs[last_cg].fd, 246 | BPF_CGROUP_INET_EGRESS); 247 | 248 | for (i = 0; i < cg_nr; i++) { 249 | bpf_link__destroy(links[i]); 250 | } 251 | test_cgroup_link__destroy(skel); 252 | 253 | for (i = 0; i < cg_nr; i++) { 254 | if (cgs[i].fd > 0) 255 | close(cgs[i].fd); 256 | } 257 | cleanup_cgroup_environment(); 258 | } 259 | -------------------------------------------------------------------------------- /src/data_breakpoint.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "data_breakpoint.skel.h" 17 | #include "data_breakpoint.h" 18 | #include "blazesym.h" 19 | 20 | /* 21 | * This function is from libbpf, but it is not a public API and can only be 22 | * used for demonstration. We can use this here because we statically link 23 | * against the libbpf built from submodule during build. 24 | */ 25 | extern int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); 26 | 27 | static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, 28 | unsigned long flags) 29 | { 30 | int ret; 31 | 32 | ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); 33 | return ret; 34 | } 35 | 36 | static struct blaze_symbolizer *symbolizer; 37 | 38 | static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info* code_info) 39 | { 40 | // If we have an input address we have a new symbol. 41 | if (input_addr != 0) { 42 | printf("%016lx: %s @ 0x%lx+0x%lx", input_addr, name, addr, offset); 43 | if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { 44 | printf(" %s/%s:%u\n", code_info->dir, code_info->file, code_info->line); 45 | } else if (code_info != NULL && code_info->file != NULL) { 46 | printf(" %s:%u\n", code_info->file, code_info->line); 47 | } else { 48 | printf("\n"); 49 | } 50 | } else { 51 | printf("%16s %s", "", name); 52 | if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { 53 | printf("@ %s/%s:%u [inlined]\n", code_info->dir, code_info->file, code_info->line); 54 | } else if (code_info != NULL && code_info->file != NULL) { 55 | printf("@ %s:%u [inlined]\n", code_info->file, code_info->line); 56 | } else { 57 | printf("[inlined]\n"); 58 | } 59 | } 60 | } 61 | 62 | static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) 63 | { 64 | const struct blaze_symbolize_inlined_fn* inlined; 65 | const struct blaze_syms *syms; 66 | const struct blaze_sym *sym; 67 | int i, j; 68 | 69 | assert(sizeof(uintptr_t) == sizeof(uint64_t)); 70 | 71 | if (pid) { 72 | struct blaze_symbolize_src_process src = { 73 | .type_size = sizeof(src), 74 | .pid = pid, 75 | }; 76 | syms = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); 77 | } else { 78 | struct blaze_symbolize_src_kernel src = { 79 | .type_size = sizeof(src), 80 | }; 81 | syms = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); 82 | } 83 | 84 | if (syms == NULL) { 85 | printf(" failed to symbolize addresses: %s\n", blaze_err_str(blaze_err_last())); 86 | return; 87 | } 88 | 89 | for (i = 0; i < stack_sz; i++) { 90 | if (!syms || syms->cnt <= i || syms->syms[i].name == NULL) { 91 | printf("%016llx: \n", stack[i]); 92 | continue; 93 | } 94 | 95 | sym = &syms->syms[i]; 96 | print_frame(sym->name, stack[i], sym->addr, sym->offset, &sym->code_info); 97 | 98 | for (j = 0; j < sym->inlined_cnt; j++) { 99 | inlined = &sym->inlined[j]; 100 | print_frame(sym->name, 0, 0, 0, &inlined->code_info); 101 | } 102 | } 103 | 104 | blaze_syms_free(syms); 105 | } 106 | 107 | /* Receive events from the ring buffer. */ 108 | static int event_handler(void *_ctx, void *data, size_t size) 109 | { 110 | struct stacktrace_event *event = data; 111 | 112 | if (event->kstack_sz <= 0 && event->ustack_sz <= 0) 113 | return 1; 114 | 115 | printf("Time:%ld, COMM: %s (pid=%d) @ CPU %d\n", time(NULL), event->comm, event->pid, event->cpu_id); 116 | 117 | if (event->kstack_sz > 0) 118 | { 119 | printf("Kernel:\n"); 120 | show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0); 121 | } 122 | else 123 | { 124 | printf("No Kernel Stack\n"); 125 | } 126 | 127 | if (event->ustack_sz > 0) 128 | { 129 | printf("Userspace:\n"); 130 | show_stack_trace(event->ustack, event->ustack_sz / sizeof(__u64), event->pid); 131 | } 132 | else 133 | { 134 | printf("No Userspace Stack\n"); 135 | } 136 | 137 | printf("\n"); 138 | return 0; 139 | } 140 | 141 | static void show_help(const char *progname) 142 | { 143 | printf("Usage: %s [-a
] [-l ] [-t ] [-h]\n", progname); 144 | } 145 | 146 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 147 | { 148 | return vfprintf(stderr, format, args); 149 | } 150 | 151 | static char *hw_type[] = {"EMPTY", "R", "W", "RW", "X"}; 152 | 153 | int main(int argc, char *const argv[]) 154 | { 155 | const char *online_cpus_file = "/sys/devices/system/cpu/online"; 156 | int pid = -1, cpu, bp_len = 1, bp_type = HW_BREAKPOINT_W; 157 | uint64_t bp_addr = 0; 158 | struct data_breakpoint_bpf *skel = NULL; 159 | struct perf_event_attr attr; 160 | struct bpf_link **links = NULL; 161 | struct ring_buffer *ring_buf = NULL; 162 | int num_cpus, num_online_cpus; 163 | int *pefds = NULL, pefd; 164 | int argp, i, err = 0; 165 | bool *online_mask = NULL; 166 | 167 | while ((argp = getopt(argc, argv, "ha:l:t:")) != -1) 168 | { 169 | switch (argp) 170 | { 171 | case 'a': 172 | bp_addr = strtoul(optarg, NULL, 16); 173 | break; 174 | case 'l': 175 | bp_len = atoi(optarg); 176 | break; 177 | case 't': 178 | bp_type = atoi(optarg); 179 | break; 180 | case 'h': 181 | default: 182 | show_help(argv[0]); 183 | return 1; 184 | } 185 | } 186 | if ((bp_len < 1 || bp_len > 8) || (bp_type < 0 || bp_type > 4)) 187 | { 188 | show_help(argv[0]); 189 | return 1; 190 | } 191 | printf("addr:%#lX, len:%d, type:%s\n", bp_addr, bp_len, hw_type[bp_type]); 192 | 193 | err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); 194 | if (err) 195 | { 196 | fprintf(stderr, "Fail to get online CPU numbers: %d\n", err); 197 | goto cleanup; 198 | } 199 | 200 | num_cpus = libbpf_num_possible_cpus(); 201 | if (num_cpus <= 0) 202 | { 203 | fprintf(stderr, "Fail to get the number of processors\n"); 204 | err = -1; 205 | goto cleanup; 206 | } 207 | libbpf_set_print(libbpf_print_fn); 208 | 209 | skel = data_breakpoint_bpf__open_and_load(); 210 | if (!skel) 211 | { 212 | fprintf(stderr, "Fail to open and load BPF skeleton\n"); 213 | err = -1; 214 | goto cleanup; 215 | } 216 | 217 | symbolizer = blaze_symbolizer_new(); 218 | if (!symbolizer) 219 | { 220 | fprintf(stderr, "Fail to create a symbolizer\n"); 221 | err = -1; 222 | goto cleanup; 223 | } 224 | 225 | /* Prepare ring buffer to receive events from the BPF program. */ 226 | ring_buf = ring_buffer__new(bpf_map__fd(skel->maps.events), event_handler, NULL, NULL); 227 | if (!ring_buf) 228 | { 229 | err = -1; 230 | goto cleanup; 231 | } 232 | 233 | pefds = malloc(num_cpus * sizeof(int)); 234 | for (i = 0; i < num_cpus; i++) 235 | { 236 | pefds[i] = -1; 237 | } 238 | 239 | links = calloc(num_cpus, sizeof(struct bpf_link *)); 240 | memset(&attr, 0, sizeof(attr)); 241 | attr.type = PERF_TYPE_BREAKPOINT; 242 | attr.size = sizeof(attr); 243 | attr.pinned = 1; 244 | attr.sample_period = 1; 245 | attr.bp_addr = bp_addr; 246 | attr.bp_len = bp_len; 247 | attr.bp_type = bp_type; 248 | 249 | for (cpu = 0; cpu < num_cpus; cpu++) 250 | { 251 | /* skip offline/not present CPUs */ 252 | if (cpu >= num_online_cpus || !online_mask[cpu]) 253 | continue; 254 | 255 | /* Set up performance monitoring on a CPU/Core */ 256 | pefd = perf_event_open(&attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); 257 | if (pefd < 0) 258 | { 259 | fprintf(stderr, "Fail to set up performance monitor on a CPU/Core,%m\n"); 260 | err = -1; 261 | goto cleanup; 262 | } 263 | pefds[cpu] = pefd; 264 | 265 | /* Attach a BPF program on a CPU */ 266 | links[cpu] = bpf_program__attach_perf_event(skel->progs.profile, pefd); 267 | if (!links[cpu]) 268 | { 269 | err = -1; 270 | goto cleanup; 271 | } 272 | } 273 | 274 | /* Wait and receive stack traces */ 275 | while (ring_buffer__poll(ring_buf, -1) >= 0) 276 | { 277 | } 278 | 279 | cleanup: 280 | if (links) 281 | { 282 | for (cpu = 0; cpu < num_cpus; cpu++) 283 | bpf_link__destroy(links[cpu]); 284 | free(links); 285 | } 286 | if (pefds) 287 | { 288 | for (i = 0; i < num_cpus; i++) 289 | { 290 | if (pefds[i] >= 0) 291 | close(pefds[i]); 292 | } 293 | free(pefds); 294 | } 295 | ring_buffer__free(ring_buf); 296 | data_breakpoint_bpf__destroy(skel); 297 | blaze_symbolizer_free(symbolizer); 298 | free(online_mask); 299 | return -err; 300 | } 301 | --------------------------------------------------------------------------------