| |
| |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "alloc-util.h" |
| #include "bpf-program.h" |
| #include "fd-util.h" |
| #include "log.h" |
| #include "missing.h" |
| #include "path-util.h" |
| #include "util.h" |
| |
| int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { |
| _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; |
| |
| p = new0(BPFProgram, 1); |
| if (!p) |
| return log_oom(); |
| |
| p->n_ref = 1; |
| p->prog_type = prog_type; |
| p->kernel_fd = -1; |
| |
| *ret = TAKE_PTR(p); |
| |
| return 0; |
| } |
| |
| BPFProgram *bpf_program_ref(BPFProgram *p) { |
| if (!p) |
| return NULL; |
| |
| assert(p->n_ref > 0); |
| p->n_ref++; |
| |
| return p; |
| } |
| |
| BPFProgram *bpf_program_unref(BPFProgram *p) { |
| if (!p) |
| return NULL; |
| |
| assert(p->n_ref > 0); |
| p->n_ref--; |
| |
| if (p->n_ref > 0) |
| return NULL; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| (void) bpf_program_cgroup_detach(p); |
| |
| safe_close(p->kernel_fd); |
| free(p->instructions); |
| free(p->attached_path); |
| |
| return mfree(p); |
| } |
| |
| int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) { |
| |
| assert(p); |
| |
| if (p->kernel_fd >= 0) |
| return -EBUSY; |
| |
| if (!GREEDY_REALLOC(p->instructions, p->allocated, p->n_instructions + count)) |
| return -ENOMEM; |
| |
| memcpy(p->instructions + p->n_instructions, instructions, sizeof(struct bpf_insn) * count); |
| p->n_instructions += count; |
| |
| return 0; |
| } |
| |
| int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) { |
| union bpf_attr attr; |
| |
| assert(p); |
| |
| if (p->kernel_fd >= 0) { |
| memzero(log_buf, log_size); |
| return 0; |
| } |
| |
| attr = (union bpf_attr) { |
| .prog_type = p->prog_type, |
| .insns = PTR_TO_UINT64(p->instructions), |
| .insn_cnt = p->n_instructions, |
| .license = PTR_TO_UINT64("GPL"), |
| .log_buf = PTR_TO_UINT64(log_buf), |
| .log_level = !!log_buf, |
| .log_size = log_size, |
| }; |
| |
| p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); |
| if (p->kernel_fd < 0) |
| return -errno; |
| |
| return 0; |
| } |
| |
| int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags) { |
| _cleanup_free_ char *copy = NULL; |
| _cleanup_close_ int fd = -1; |
| union bpf_attr attr; |
| int r; |
| |
| assert(p); |
| assert(type >= 0); |
| assert(path); |
| |
| if (!IN_SET(flags, 0, BPF_F_ALLOW_OVERRIDE, BPF_F_ALLOW_MULTI)) |
| return -EINVAL; |
| |
| |
| |
| if (p->attached_path) { |
| if (!path_equal(p->attached_path, path)) |
| return -EBUSY; |
| if (p->attached_type != type) |
| return -EBUSY; |
| if (p->attached_flags != flags) |
| return -EBUSY; |
| |
| |
| |
| |
| |
| |
| |
| if (flags != BPF_F_ALLOW_OVERRIDE) |
| return 0; |
| } |
| |
| |
| r = bpf_program_load_kernel(p, NULL, 0); |
| if (r < 0) |
| return r; |
| |
| copy = strdup(path); |
| if (!copy) |
| return -ENOMEM; |
| |
| fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC); |
| if (fd < 0) |
| return -errno; |
| |
| attr = (union bpf_attr) { |
| .attach_type = type, |
| .target_fd = fd, |
| .attach_bpf_fd = p->kernel_fd, |
| .attach_flags = flags, |
| }; |
| |
| if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) |
| return -errno; |
| |
| free_and_replace(p->attached_path, copy); |
| p->attached_type = type; |
| p->attached_flags = flags; |
| |
| return 0; |
| } |
| |
| int bpf_program_cgroup_detach(BPFProgram *p) { |
| _cleanup_close_ int fd = -1; |
| |
| assert(p); |
| |
| if (!p->attached_path) |
| return -EUNATCH; |
| |
| fd = open(p->attached_path, O_DIRECTORY|O_RDONLY|O_CLOEXEC); |
| if (fd < 0) { |
| if (errno != ENOENT) |
| return -errno; |
| |
| |
| |
| |
| } else { |
| union bpf_attr attr; |
| |
| attr = (union bpf_attr) { |
| .attach_type = p->attached_type, |
| .target_fd = fd, |
| .attach_bpf_fd = p->kernel_fd, |
| }; |
| |
| if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0) |
| return -errno; |
| } |
| |
| p->attached_path = mfree(p->attached_path); |
| |
| return 0; |
| } |
| |
| int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags) { |
| union bpf_attr attr = { |
| .map_type = type, |
| .key_size = key_size, |
| .value_size = value_size, |
| .max_entries = max_entries, |
| .map_flags = flags, |
| }; |
| int fd; |
| |
| fd = bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); |
| if (fd < 0) |
| return -errno; |
| |
| return fd; |
| } |
| |
| int bpf_map_update_element(int fd, const void *key, void *value) { |
| |
| union bpf_attr attr = { |
| .map_fd = fd, |
| .key = PTR_TO_UINT64(key), |
| .value = PTR_TO_UINT64(value), |
| }; |
| |
| if (bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)) < 0) |
| return -errno; |
| |
| return 0; |
| } |
| |
| int bpf_map_lookup_element(int fd, const void *key, void *value) { |
| |
| union bpf_attr attr = { |
| .map_fd = fd, |
| .key = PTR_TO_UINT64(key), |
| .value = PTR_TO_UINT64(value), |
| }; |
| |
| if (bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)) < 0) |
| return -errno; |
| |
| return 0; |
| } |