From 71e5200f94b22589922704aa4abdf95d4fe2e528 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 18 Oct 2016 17:57:10 +0200 Subject: [PATCH] Add abstraction model for BPF programs This object takes a number of bpf_insn members and wraps them together with the in-kernel reference id. Will be needed by the firewall code. --- meson.build | 2 + src/basic/bpf-program.c | 182 ++++++++++++++++++++++++++++++++++++ src/basic/bpf-program.h | 55 +++++++++++ src/basic/meson.build | 20 ++-- src/basic/missing_syscall.h | 32 +++++++ 5 files changed, 282 insertions(+), 9 deletions(-) create mode 100644 src/basic/bpf-program.c create mode 100644 src/basic/bpf-program.h diff --git a/meson.build b/meson.build index 3e85442a6f..d72fc6f148 100644 --- a/meson.build +++ b/meson.build @@ -443,6 +443,8 @@ foreach ident : [ #include '''], ['copy_file_range', '''#include #include '''], + ['bpf', '''#include + #include '''], ['explicit_bzero' , '''#include '''], ] diff --git a/src/basic/bpf-program.c b/src/basic/bpf-program.c new file mode 100644 index 0000000000..9326176743 --- /dev/null +++ b/src/basic/bpf-program.c @@ -0,0 +1,182 @@ +/*** + This file is part of systemd. + + Copyright 2016 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "alloc-util.h" +#include "bpf-program.h" +#include "fd-util.h" +#include "log.h" +#include "missing.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->prog_type = prog_type; + p->kernel_fd = -1; + + *ret = p; + p = NULL; + return 0; +} + +BPFProgram *bpf_program_unref(BPFProgram *p) { + if (!p) + return NULL; + + safe_close(p->kernel_fd); + free(p->instructions); + + return mfree(p); +} + +int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *instructions, size_t count) { + + assert(p); + + 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) + return -EBUSY; + + 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) { + _cleanup_close_ int fd = -1; + union bpf_attr attr; + + assert(p); + assert(type >= 0); + assert(path); + + 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, + }; + + if (bpf(BPF_PROG_ATTACH, &attr, sizeof(attr)) < 0) + return -errno; + + return 0; +} + +int bpf_program_cgroup_detach(int type, const char *path) { + _cleanup_close_ int fd = -1; + union bpf_attr attr; + + assert(path); + + fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + attr = (union bpf_attr) { + .attach_type = type, + .target_fd = fd, + }; + + if (bpf(BPF_PROG_DETACH, &attr, sizeof(attr)) < 0) + return -errno; + + 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; +} diff --git a/src/basic/bpf-program.h b/src/basic/bpf-program.h new file mode 100644 index 0000000000..0dd150b60a --- /dev/null +++ b/src/basic/bpf-program.h @@ -0,0 +1,55 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + + [Except for the stuff copy/pasted from the kernel sources, see below] +***/ + +#include +#include +#include + +#include "list.h" +#include "macro.h" + +typedef struct BPFProgram BPFProgram; + +struct BPFProgram { + int kernel_fd; + uint32_t prog_type; + + size_t n_instructions; + size_t allocated; + struct bpf_insn *instructions; +}; + +int bpf_program_new(uint32_t prog_type, BPFProgram **ret); +BPFProgram *bpf_program_unref(BPFProgram *p); + +int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count); +int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size); + +int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path); +int bpf_program_cgroup_detach(int type, const char *path); + +int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags); +int bpf_map_update_element(int fd, const void *key, void *value); +int bpf_map_lookup_element(int fd, const void *key, void *value); + +DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref); diff --git a/src/basic/meson.build b/src/basic/meson.build index 67cc27274d..994336fde2 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -1,4 +1,6 @@ basic_sources_plain = files(''' + MurmurHash2.c + MurmurHash2.h af-list.c af-list.h alloc-util.c @@ -16,6 +18,8 @@ basic_sources_plain = files(''' bitmap.c bitmap.h blkid-util.h + bpf-program.c + bpf-program.h btrfs-ctree.h btrfs-util.c btrfs-util.h @@ -24,10 +28,10 @@ basic_sources_plain = files(''' bus-label.h calendarspec.c calendarspec.h - capability-util.c - capability-util.h cap-list.c cap-list.h + capability-util.c + capability-util.h cgroup-util.c cgroup-util.h chattr-util.c @@ -61,10 +65,10 @@ basic_sources_plain = files(''' extract-word.h fd-util.c fd-util.h - fileio.c - fileio.h fileio-label.c fileio-label.h + fileio.c + fileio.h format-util.h fs-util.c fs-util.h @@ -82,9 +86,9 @@ basic_sources_plain = files(''' hostname-util.h in-addr-util.c in-addr-util.h - ioprio.h io-util.c io-util.h + ioprio.h journal-importer.c journal-importer.h khash.c @@ -106,13 +110,11 @@ basic_sources_plain = files(''' mempool.c mempool.h missing_syscall.h + mkdir-label.c mkdir.c mkdir.h - mkdir-label.c mount-util.c mount-util.h - MurmurHash2.c - MurmurHash2.h nss-util.h ordered-set.c ordered-set.h @@ -138,9 +140,9 @@ basic_sources_plain = files(''' rlimit-util.h rm-rf.c rm-rf.h - securebits.h securebits-util.c securebits-util.h + securebits.h selinux-util.c selinux-util.h set.h diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 898116c7b3..17cde5e74f 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -22,6 +22,8 @@ /* Missing glibc definitions to access certain kernel APIs */ +#include + #if !HAVE_DECL_PIVOT_ROOT static inline int pivot_root(const char *new_root, const char *put_old) { return syscall(SYS_pivot_root, new_root, put_old); @@ -316,3 +318,33 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, # endif } #endif + +#if !HAVE_DECL_BPF +# ifndef __NR_bpf +# if defined __i386__ +# define __NR_bpf 357 +# elif defined __x86_64__ +# define __NR_bpf 321 +# elif defined __aarch64__ +# define __NR_bpf 280 +# elif defined __sparc__ +# define __NR_bpf 349 +# elif defined __s390__ +# define __NR_bpf 351 +# else +# warning "__NR_bpf not defined for your architecture" +# endif +# endif + +union bpf_attr; + +static inline int bpf(int cmd, union bpf_attr *attr, size_t size) { +#ifdef __NR_bpf + return (int) syscall(__NR_bpf, cmd, attr, size); +#else + errno = ENOSYS; + return -1; +#endif +} + +#endif