Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 1 | #include <time.h> |
| 2 | #include <stdio.h> |
| 3 | #include <errno.h> |
| 4 | #include <string.h> |
| 5 | #include <stdlib.h> |
| 6 | #include <stdio.h> |
| 7 | #include <stdint.h> |
| 8 | #include <unistd.h> |
| 9 | #include <ctype.h> |
| 10 | #include <sysexits.h> |
| 11 | |
| 12 | #include "libbpf.h" |
| 13 | |
nikolay.samofatov | c5308e9 | 2017-12-28 19:01:31 +0300 | [diff] [blame] | 14 | // TODO: Remove this when CentOS 6 support is not needed anymore |
| 15 | #ifndef CLOCK_BOOTTIME |
| 16 | #define CLOCK_BOOTTIME 7 |
| 17 | #endif |
| 18 | |
Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 19 | static const char * const prog_type_strings[] = { |
| 20 | [BPF_PROG_TYPE_UNSPEC] = "unspec", |
| 21 | [BPF_PROG_TYPE_SOCKET_FILTER] = "socket filter", |
| 22 | [BPF_PROG_TYPE_KPROBE] = "kprobe", |
| 23 | [BPF_PROG_TYPE_SCHED_CLS] = "sched cls", |
| 24 | [BPF_PROG_TYPE_SCHED_ACT] = "sched act", |
| 25 | [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", |
| 26 | [BPF_PROG_TYPE_XDP] = "xdp", |
| 27 | [BPF_PROG_TYPE_PERF_EVENT] = "perf event", |
| 28 | [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup skb", |
| 29 | [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup sock", |
| 30 | [BPF_PROG_TYPE_LWT_IN] = "lwt in", |
| 31 | [BPF_PROG_TYPE_LWT_OUT] = "lwt out", |
| 32 | [BPF_PROG_TYPE_LWT_XMIT] = "lwt xmit", |
| 33 | [BPF_PROG_TYPE_SOCK_OPS] = "sock ops", |
| 34 | [BPF_PROG_TYPE_SK_SKB] = "sk skb", |
Yonghong Song | 6f3c825 | 2018-06-10 18:04:11 -0700 | [diff] [blame] | 35 | [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", |
| 36 | [BPF_PROG_TYPE_SK_MSG] = "sk_msg", |
| 37 | [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", |
| 38 | [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", |
| 39 | [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2", |
yonghong-song | 3835b2d | 2018-08-16 18:49:06 -0700 | [diff] [blame] | 40 | [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport", |
yonghong-song | d4b2387 | 2018-10-10 16:48:15 -0700 | [diff] [blame] | 41 | [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", |
Yonghong Song | 912d357 | 2019-04-29 11:43:22 -0700 | [diff] [blame] | 42 | [BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl", |
Yonghong Song | 3bdeff3 | 2019-05-17 10:26:44 -0700 | [diff] [blame] | 43 | [BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE] = "raw_tracepoint_writable", |
Yonghong Song | 06e30cc | 2019-07-09 22:54:31 -0700 | [diff] [blame] | 44 | [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt", |
yonghong-song | 992e482 | 2019-11-13 13:37:58 -0800 | [diff] [blame] | 45 | [BPF_PROG_TYPE_TRACING] = "tracing", |
Yonghong Song | 071eef6 | 2020-01-10 12:52:44 -0800 | [diff] [blame] | 46 | [BPF_PROG_TYPE_STRUCT_OPS] = "struct_ops", |
Yonghong Song | 83b67c2 | 2020-01-27 14:35:21 -0800 | [diff] [blame] | 47 | [BPF_PROG_TYPE_EXT] = "ext", |
Yonghong Song | df3e7d6 | 2020-04-20 09:25:57 -0700 | [diff] [blame] | 48 | [BPF_PROG_TYPE_LSM] = "lsm", |
Yonghong Song | 8a164f9 | 2020-07-30 11:32:49 -0700 | [diff] [blame] | 49 | [BPF_PROG_TYPE_SK_LOOKUP] = "sk_lookup", |
yonghong-song | 949a4e5 | 2021-07-18 15:05:34 -0700 | [diff] [blame] | 50 | [BPF_PROG_TYPE_SYSCALL] = "syscall", |
Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 51 | }; |
| 52 | |
| 53 | static const char * const map_type_strings[] = { |
| 54 | [BPF_MAP_TYPE_UNSPEC] = "unspec", |
| 55 | [BPF_MAP_TYPE_HASH] = "hash", |
| 56 | [BPF_MAP_TYPE_ARRAY] = "array", |
| 57 | [BPF_MAP_TYPE_PROG_ARRAY] = "prog array", |
| 58 | [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf-ev array", |
| 59 | [BPF_MAP_TYPE_PERCPU_HASH] = "percpu hash", |
| 60 | [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu array", |
| 61 | [BPF_MAP_TYPE_STACK_TRACE] = "stack trace", |
| 62 | [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup array", |
| 63 | [BPF_MAP_TYPE_LRU_HASH] = "lru hash", |
| 64 | [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru percpu hash", |
| 65 | [BPF_MAP_TYPE_LPM_TRIE] = "lpm trie", |
| 66 | [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array of maps", |
| 67 | [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash of maps", |
| 68 | [BPF_MAP_TYPE_DEVMAP] = "devmap", |
| 69 | [BPF_MAP_TYPE_SOCKMAP] = "sockmap", |
Yonghong Song | 6f3c825 | 2018-06-10 18:04:11 -0700 | [diff] [blame] | 70 | [BPF_MAP_TYPE_CPUMAP] = "cpumap", |
| 71 | [BPF_MAP_TYPE_SOCKHASH] = "sockhash", |
yonghong-song | 3835b2d | 2018-08-16 18:49:06 -0700 | [diff] [blame] | 72 | [BPF_MAP_TYPE_CGROUP_STORAGE] = "cgroup_storage", |
| 73 | [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray", |
Yonghong Song | 2a68bd8 | 2018-10-27 23:14:52 -0700 | [diff] [blame] | 74 | [BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE] = "precpu_cgroup_storage", |
| 75 | [BPF_MAP_TYPE_QUEUE] = "queue", |
| 76 | [BPF_MAP_TYPE_STACK] = "stack", |
Yonghong Song | 3bdeff3 | 2019-05-17 10:26:44 -0700 | [diff] [blame] | 77 | [BPF_MAP_TYPE_SK_STORAGE] = "sk_storage", |
yonghong-song | eb018cf | 2019-10-02 16:34:38 -0700 | [diff] [blame] | 78 | [BPF_MAP_TYPE_DEVMAP_HASH] = "devmap_hash", |
Yonghong Song | 071eef6 | 2020-01-10 12:52:44 -0800 | [diff] [blame] | 79 | [BPF_MAP_TYPE_STRUCT_OPS] = "struct_ops", |
Yonghong Song | 99fa312 | 2020-06-16 00:03:11 -0700 | [diff] [blame] | 80 | [BPF_MAP_TYPE_RINGBUF] = "ringbuf", |
Yonghong Song | d0e1c93 | 2020-10-01 17:32:16 -0700 | [diff] [blame] | 81 | [BPF_MAP_TYPE_INODE_STORAGE] = "inode_storage", |
Yonghong Song | 1f4b907 | 2021-01-04 20:11:54 -0800 | [diff] [blame] | 82 | [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage", |
Yonghong Song | 9d6776b | 2021-11-17 15:57:02 -0800 | [diff] [blame] | 83 | [BPF_MAP_TYPE_BLOOM_FILTER] = "bloom_filter", |
Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 84 | }; |
| 85 | |
| 86 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) |
| 87 | #define LAST_KNOWN_PROG_TYPE (ARRAY_SIZE(prog_type_strings) - 1) |
| 88 | #define LAST_KNOWN_MAP_TYPE (ARRAY_SIZE(map_type_strings) - 1) |
| 89 | #define min(x, y) ((x) < (y) ? (x) : (y)) |
| 90 | |
| 91 | static inline uint64_t ptr_to_u64(const void *ptr) |
| 92 | { |
| 93 | return (uint64_t) (unsigned long) ptr; |
| 94 | } |
| 95 | |
| 96 | static inline void * u64_to_ptr(uint64_t ptr) |
| 97 | { |
| 98 | return (void *) (unsigned long ) ptr; |
| 99 | } |
| 100 | |
| 101 | static int handle_get_next_errno(int eno) |
| 102 | { |
| 103 | switch (eno) { |
| 104 | case ENOENT: |
| 105 | return 0; |
| 106 | case EINVAL: |
| 107 | fprintf(stderr, "Kernel does not support BPF introspection\n"); |
| 108 | return EX_UNAVAILABLE; |
| 109 | case EPERM: |
| 110 | fprintf(stderr, |
| 111 | "Require CAP_SYS_ADMIN capability. Please retry as root\n"); |
| 112 | return EX_NOPERM; |
| 113 | default: |
| 114 | fprintf(stderr, "%s\n", strerror(errno)); |
| 115 | return 1; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | static void print_prog_hdr(void) |
| 120 | { |
| 121 | printf("%9s %-15s %8s %6s %-12s %-15s\n", |
| 122 | "BID", "TYPE", "UID", "#MAPS", "LoadTime", "NAME"); |
| 123 | } |
| 124 | |
| 125 | static void print_prog_info(const struct bpf_prog_info *prog_info) |
| 126 | { |
| 127 | struct timespec real_time_ts, boot_time_ts; |
| 128 | time_t wallclock_load_time = 0; |
| 129 | char unknown_prog_type[16]; |
| 130 | const char *prog_type; |
| 131 | char load_time[16]; |
| 132 | struct tm load_tm; |
| 133 | |
| 134 | if (prog_info->type > LAST_KNOWN_PROG_TYPE) { |
| 135 | snprintf(unknown_prog_type, sizeof(unknown_prog_type), "<%u>", |
| 136 | prog_info->type); |
| 137 | unknown_prog_type[sizeof(unknown_prog_type) - 1] = '\0'; |
| 138 | prog_type = unknown_prog_type; |
| 139 | } else { |
| 140 | prog_type = prog_type_strings[prog_info->type]; |
| 141 | } |
| 142 | |
| 143 | if (!clock_gettime(CLOCK_REALTIME, &real_time_ts) && |
| 144 | !clock_gettime(CLOCK_BOOTTIME, &boot_time_ts) && |
| 145 | real_time_ts.tv_sec >= boot_time_ts.tv_sec) |
| 146 | wallclock_load_time = |
| 147 | (real_time_ts.tv_sec - boot_time_ts.tv_sec) + |
| 148 | prog_info->load_time / 1000000000; |
| 149 | |
| 150 | if (wallclock_load_time && localtime_r(&wallclock_load_time, &load_tm)) |
| 151 | strftime(load_time, sizeof(load_time), "%b%d/%H:%M", &load_tm); |
| 152 | else |
| 153 | snprintf(load_time, sizeof(load_time), "<%llu>", |
| 154 | prog_info->load_time / 1000000000); |
| 155 | load_time[sizeof(load_time) - 1] = '\0'; |
| 156 | |
| 157 | if (prog_info->jited_prog_len) |
| 158 | printf("%9u %-15s %8u %6u %-12s %-15s\n", |
| 159 | prog_info->id, prog_type, prog_info->created_by_uid, |
| 160 | prog_info->nr_map_ids, load_time, prog_info->name); |
| 161 | else |
| 162 | printf("%8u- %-15s %8u %6u %-12s %-15s\n", |
| 163 | prog_info->id, prog_type, prog_info->created_by_uid, |
| 164 | prog_info->nr_map_ids, load_time, prog_info->name); |
| 165 | } |
| 166 | |
| 167 | static void print_map_hdr(void) |
| 168 | { |
| 169 | printf("%8s %-15s %-10s %8s %8s %8s %-15s\n", |
| 170 | "MID", "TYPE", "FLAGS", "KeySz", "ValueSz", "MaxEnts", |
| 171 | "NAME"); |
| 172 | } |
| 173 | |
| 174 | static void print_map_info(const struct bpf_map_info *map_info) |
| 175 | { |
| 176 | char unknown_map_type[16]; |
| 177 | const char *map_type; |
| 178 | |
| 179 | if (map_info->type > LAST_KNOWN_MAP_TYPE) { |
| 180 | snprintf(unknown_map_type, sizeof(unknown_map_type), |
| 181 | "<%u>", map_info->type); |
| 182 | unknown_map_type[sizeof(unknown_map_type) - 1] = '\0'; |
| 183 | map_type = unknown_map_type; |
| 184 | } else { |
| 185 | map_type = map_type_strings[map_info->type]; |
| 186 | } |
| 187 | |
| 188 | printf("%8u %-15s 0x%-8x %8u %8u %8u %-15s\n", |
| 189 | map_info->id, map_type, map_info->map_flags, map_info->key_size, |
| 190 | map_info->value_size, map_info->max_entries, |
| 191 | map_info->name); |
| 192 | } |
| 193 | |
| 194 | static int print_one_prog(uint32_t prog_id) |
| 195 | { |
| 196 | const uint32_t usual_nr_map_ids = 64; |
| 197 | uint32_t nr_map_ids = usual_nr_map_ids; |
| 198 | struct bpf_prog_info prog_info; |
| 199 | uint32_t *map_ids = NULL; |
| 200 | uint32_t info_len; |
| 201 | int ret = 0; |
| 202 | int prog_fd; |
| 203 | uint32_t i; |
| 204 | |
| 205 | prog_fd = bpf_prog_get_fd_by_id(prog_id); |
| 206 | if (prog_fd == -1) { |
| 207 | if (errno == ENOENT) { |
| 208 | fprintf(stderr, "BID:%u not found\n", prog_id); |
| 209 | return EX_DATAERR; |
| 210 | } else { |
| 211 | return handle_get_next_errno(errno); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | /* Retry at most one time for larger map_ids array */ |
| 216 | for (i = 0; i < 2; i++) { |
| 217 | bzero(&prog_info, sizeof(prog_info)); |
| 218 | prog_info.map_ids = ptr_to_u64(realloc(map_ids, |
| 219 | nr_map_ids * sizeof(*map_ids))); |
| 220 | if (!prog_info.map_ids) { |
| 221 | fprintf(stderr, |
| 222 | "Cannot allocate memory for %u map_ids for BID:%u\n", |
| 223 | nr_map_ids, prog_id); |
| 224 | close(prog_fd); |
| 225 | free(map_ids); |
| 226 | return 1; |
| 227 | } |
| 228 | |
| 229 | map_ids = u64_to_ptr(prog_info.map_ids); |
| 230 | prog_info.nr_map_ids = nr_map_ids; |
| 231 | info_len = sizeof(prog_info); |
| 232 | ret = bpf_obj_get_info(prog_fd, &prog_info, &info_len); |
| 233 | if (ret) { |
| 234 | fprintf(stderr, "Cannot get info for BID:%u. %s(%d)\n", |
| 235 | prog_id, strerror(errno), errno); |
| 236 | close(prog_fd); |
| 237 | free(map_ids); |
| 238 | return ret; |
| 239 | } |
| 240 | |
| 241 | if (prog_info.nr_map_ids <= nr_map_ids) |
| 242 | break; |
| 243 | |
| 244 | nr_map_ids = prog_info.nr_map_ids; |
| 245 | } |
| 246 | close(prog_fd); |
| 247 | |
| 248 | print_prog_hdr(); |
| 249 | print_prog_info(&prog_info); |
| 250 | printf("\n"); |
| 251 | |
| 252 | /* Print all map_info used by the prog */ |
| 253 | print_map_hdr(); |
| 254 | nr_map_ids = min(prog_info.nr_map_ids, nr_map_ids); |
| 255 | for (i = 0; i < nr_map_ids; i++) { |
| 256 | struct bpf_map_info map_info = {}; |
Teng Qin | d77a41b | 2017-10-31 12:05:17 -0700 | [diff] [blame] | 257 | info_len = sizeof(map_info); |
Martin KaFai Lau | f89cb40 | 2017-10-19 23:52:54 -0700 | [diff] [blame] | 258 | int map_fd; |
| 259 | |
| 260 | map_fd = bpf_map_get_fd_by_id(map_ids[i]); |
| 261 | if (map_fd == -1) { |
| 262 | if (errno == -ENOENT) |
| 263 | continue; |
| 264 | |
| 265 | fprintf(stderr, |
| 266 | "Cannot get fd for map:%u. %s(%d)\n", |
| 267 | map_ids[i], strerror(errno), errno); |
| 268 | ret = map_fd; |
| 269 | break; |
| 270 | } |
| 271 | |
| 272 | ret = bpf_obj_get_info(map_fd, &map_info, &info_len); |
| 273 | close(map_fd); |
| 274 | if (ret) { |
| 275 | fprintf(stderr, "Cannot get info for map:%u. %s(%d)\n", |
| 276 | map_ids[i], strerror(errno), errno); |
| 277 | break; |
| 278 | } |
| 279 | |
| 280 | print_map_info(&map_info); |
| 281 | } |
| 282 | |
| 283 | free(map_ids); |
| 284 | return ret; |
| 285 | } |
| 286 | |
| 287 | int print_all_progs(void) |
| 288 | { |
| 289 | uint32_t next_id = 0; |
| 290 | |
| 291 | print_prog_hdr(); |
| 292 | |
| 293 | while (!bpf_prog_get_next_id(next_id, &next_id)) { |
| 294 | struct bpf_prog_info prog_info = {}; |
| 295 | uint32_t prog_info_len = sizeof(prog_info); |
| 296 | int prog_fd; |
| 297 | int ret; |
| 298 | |
| 299 | prog_fd = bpf_prog_get_fd_by_id(next_id); |
| 300 | if (prog_fd < 0) { |
| 301 | if (errno == ENOENT) |
| 302 | continue; |
| 303 | fprintf(stderr, |
| 304 | "Cannot get fd for BID:%u. %s(%d)\n", |
| 305 | next_id, strerror(errno), errno); |
| 306 | return 1; |
| 307 | } |
| 308 | |
| 309 | ret = bpf_obj_get_info(prog_fd, &prog_info, &prog_info_len); |
| 310 | close(prog_fd); |
| 311 | if (ret) { |
| 312 | fprintf(stderr, |
| 313 | "Cannot get bpf_prog_info for BID:%u. %s(%d)\n", |
| 314 | next_id, strerror(errno), errno); |
| 315 | return ret; |
| 316 | } |
| 317 | |
| 318 | print_prog_info(&prog_info); |
| 319 | } |
| 320 | |
| 321 | return handle_get_next_errno(errno); |
| 322 | } |
| 323 | |
| 324 | void usage(void) |
| 325 | { |
| 326 | printf("BPF Program Snapshot (bps):\n" |
| 327 | "List of all BPF programs loaded into the system.\n\n"); |
| 328 | printf("Usage: bps [bpf-prog-id]\n"); |
| 329 | printf(" [bpf-prog-id] If specified, it shows the details info of the bpf-prog\n"); |
| 330 | printf("\n"); |
| 331 | } |
| 332 | |
| 333 | int main(int argc, char **argv) |
| 334 | { |
| 335 | if (argc > 1) { |
| 336 | if (!isdigit(*argv[1])) { |
| 337 | usage(); |
| 338 | return EX_USAGE; |
| 339 | } |
| 340 | return print_one_prog((uint32_t)atoi(argv[1])); |
| 341 | } |
| 342 | |
| 343 | return print_all_progs(); |
| 344 | } |