summaryrefslogtreecommitdiffstats
path: root/xdp-loader/xdp-loader.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:10:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 07:10:00 +0000
commit4ba2b326284765e942044db13a7f0dae702bec93 (patch)
treecbdfaec33eed4f3a970c54cd10e8ddfe3003b3b1 /xdp-loader/xdp-loader.c
parentInitial commit. (diff)
downloadxdp-tools-4ba2b326284765e942044db13a7f0dae702bec93.tar.xz
xdp-tools-4ba2b326284765e942044db13a7f0dae702bec93.zip
Adding upstream version 1.3.1.upstream/1.3.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xdp-loader/xdp-loader.c')
-rw-r--r--xdp-loader/xdp-loader.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/xdp-loader/xdp-loader.c b/xdp-loader/xdp-loader.c
new file mode 100644
index 0000000..aed52a7
--- /dev/null
+++ b/xdp-loader/xdp-loader.c
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+#include <xdp/libxdp.h>
+#include <linux/err.h>
+
+#include "params.h"
+#include "logging.h"
+#include "util.h"
+
+#define PROG_NAME "xdp-loader"
+
+static const struct loadopt {
+ bool help;
+ struct iface iface;
+ struct multistring filenames;
+ char *pin_path;
+ char *section_name;
+ char *prog_name;
+ enum xdp_attach_mode mode;
+ __u32 prio;
+ __u32 actions;
+} defaults_load = {
+ .mode = XDP_MODE_NATIVE
+};
+
+struct enum_val xdp_modes[] = {
+ {"native", XDP_MODE_NATIVE},
+ {"skb", XDP_MODE_SKB},
+ {"hw", XDP_MODE_HW},
+ {"unspecified", XDP_MODE_UNSPEC},
+ {NULL, 0}
+};
+
+struct flag_val load_actions[] = {
+ {"XDP_ABORTED", 1U << XDP_ABORTED},
+ {"XDP_DROP", 1U << XDP_DROP},
+ {"XDP_PASS", 1U << XDP_PASS},
+ {"XDP_TX", 1U << XDP_TX},
+ {"XDP_REDIRECT", 1U << XDP_REDIRECT},
+ {}
+};
+
+static struct prog_option load_options[] = {
+ DEFINE_OPTION("mode", OPT_ENUM, struct loadopt, mode,
+ .short_opt = 'm',
+ .typearg = xdp_modes,
+ .metavar = "<mode>",
+ .help = "Load XDP program in <mode>; default native"),
+ DEFINE_OPTION("pin-path", OPT_STRING, struct loadopt, pin_path,
+ .short_opt = 'p',
+ .help = "Path to pin maps under (must be in bpffs)."),
+ DEFINE_OPTION("section", OPT_STRING, struct loadopt, section_name,
+ .metavar = "<section>",
+ .short_opt = 's',
+ .help = "ELF section name of program to load (default: first in file)."),
+ DEFINE_OPTION("prog-name", OPT_STRING, struct loadopt, prog_name,
+ .metavar = "<prog_name>",
+ .short_opt = 'n',
+ .help = "BPF program name of program to load (default: first in file)."),
+ DEFINE_OPTION("dev", OPT_IFNAME, struct loadopt, iface,
+ .positional = true,
+ .metavar = "<ifname>",
+ .required = true,
+ .help = "Load on device <ifname>"),
+ DEFINE_OPTION("filenames", OPT_MULTISTRING, struct loadopt, filenames,
+ .positional = true,
+ .metavar = "<filenames>",
+ .required = true,
+ .help = "Load programs from <filenames>"),
+ DEFINE_OPTION("prio", OPT_U32, struct loadopt, prio,
+ .short_opt = 'P',
+ .help = "Set run priority of program"),
+ DEFINE_OPTION("actions", OPT_FLAGS, struct loadopt, actions,
+ .short_opt = 'A',
+ .typearg = load_actions,
+ .metavar = "<actions>",
+ .help = "Chain call actions (default: XDP_PASS). e.g. XDP_PASS,XDP_DROP"),
+ END_OPTIONS
+};
+
+int do_load(const void *cfg, __unused const char *pin_root_path)
+{
+ const struct loadopt *opt = cfg;
+ struct xdp_program **progs, *p;
+ char errmsg[STRERR_BUFSIZE];
+ int err = EXIT_SUCCESS;
+ size_t num_progs, i;
+ DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .pin_root_path = opt->pin_path);
+
+ if (opt->section_name && opt->prog_name) {
+ pr_warn("Only one of --section or --prog-name can be set\n");
+ return EXIT_FAILURE;
+ }
+
+ num_progs = opt->filenames.num_strings;
+ if (!num_progs) {
+ pr_warn("Need at least one filename to load\n");
+ return EXIT_FAILURE;
+ } else if (num_progs > 1 && opt->mode == XDP_MODE_HW) {
+ pr_warn("Cannot attach multiple programs in HW mode\n");
+ return EXIT_FAILURE;
+ }
+
+ progs = calloc(num_progs, sizeof(*progs));
+ if (!progs) {
+ pr_warn("Couldn't allocate memory\n");
+ return EXIT_FAILURE;
+ }
+
+ pr_debug("Loading %zu files on interface '%s'.\n",
+ num_progs, opt->iface.ifname);
+
+ /* libbpf spits out a lot of unhelpful error messages while loading.
+ * Silence the logging so we can provide our own messages instead; this
+ * is a noop if verbose logging is enabled.
+ */
+ silence_libbpf_logging();
+
+retry:
+ for (i = 0; i < num_progs; i++) {
+ DECLARE_LIBXDP_OPTS(xdp_program_opts, xdp_opts, 0);
+ struct bpf_program *bpf_prog = NULL;
+
+ p = progs[i];
+ if (p)
+ xdp_program__close(p);
+
+ if (opt->prog_name) {
+ xdp_opts.open_filename = opt->filenames.strings[i];
+ xdp_opts.prog_name = opt->prog_name;
+ xdp_opts.opts = &opts;
+
+ p = xdp_program__create(&xdp_opts);
+ } else {
+ p = xdp_program__open_file(opt->filenames.strings[i],
+ opt->section_name, &opts);
+ }
+
+ err = libxdp_get_error(p);
+ if (err) {
+ if (err == -EPERM && !double_rlimit())
+ goto retry;
+
+ libxdp_strerror(err, errmsg, sizeof(errmsg));
+ pr_warn("Couldn't open file '%s': %s\n",
+ opt->filenames.strings[i], errmsg);
+ goto out;
+ }
+
+ /* Disable autoload for all programs in the bpf object; libxdp
+ * will make sure to turn it back on for the program that we're
+ * actually loading
+ */
+ bpf_object__for_each_program(bpf_prog, xdp_program__bpf_obj(p))
+ bpf_program__set_autoload(bpf_prog, false);
+
+ if (opt->prio) {
+ err = xdp_program__set_run_prio(p, opt->prio);
+ if (err) {
+ pr_warn("Error setting run priority: %u\n", opt->prio);
+ goto out;
+ }
+ }
+
+ if (opt->actions) {
+ __u32 a;
+
+ for (a = XDP_ABORTED; a <= XDP_REDIRECT; a++) {
+ err = xdp_program__set_chain_call_enabled(p, a, opt->actions & (1U << a));
+ if (err) {
+ pr_warn("Error setting chain call action: %u\n", a);
+ goto out;
+ }
+ }
+ }
+
+ xdp_program__print_chain_call_actions(p, errmsg, sizeof(errmsg));
+ pr_debug("XDP program %zu: Run prio: %d. Chain call actions: %s\n",
+ i, xdp_program__run_prio(p), errmsg);
+
+ if (!opt->pin_path) {
+ struct bpf_map *map;
+
+ bpf_object__for_each_map(map, xdp_program__bpf_obj(p)) {
+ err = bpf_map__set_pin_path(map, NULL);
+ if (err) {
+ pr_warn("Error clearing map pin path: %s\n",
+ strerror(-err));
+ goto out;
+ }
+ }
+ }
+
+ progs[i] = p;
+ }
+
+ err = xdp_program__attach_multi(progs, num_progs,
+ opt->iface.ifindex, opt->mode, 0);
+ if (err) {
+ if (err == -EPERM && !double_rlimit())
+ goto retry;
+
+ if (err == -EOPNOTSUPP &&
+ (opt->mode == XDP_MODE_NATIVE || opt->mode == XDP_MODE_HW)) {
+ pr_warn("Attaching XDP program in %s mode not supported - try %s mode.\n",
+ opt->mode == XDP_MODE_NATIVE ? "native" : "HW",
+ opt->mode == XDP_MODE_NATIVE ? "SKB" : "native or SKB");
+ } else {
+ libbpf_strerror(err, errmsg, sizeof(errmsg));
+ pr_warn("Couldn't attach XDP program on iface '%s': %s(%d)\n",
+ opt->iface.ifname, errmsg, err);
+ }
+ goto out;
+ }
+
+out:
+ for (i = 0; i < num_progs; i++)
+ if (progs[i])
+ xdp_program__close(progs[i]);
+ free(progs);
+ return err;
+}
+
+static const struct unloadopt {
+ bool all;
+ __u32 prog_id;
+ struct iface iface;
+} defaults_unload = {};
+
+static struct prog_option unload_options[] = {
+ DEFINE_OPTION("dev", OPT_IFNAME, struct unloadopt, iface,
+ .positional = true,
+ .metavar = "<ifname>",
+ .help = "Unload from device <ifname>"),
+ DEFINE_OPTION("id", OPT_U32, struct unloadopt, prog_id,
+ .metavar = "<id>",
+ .short_opt = 'i',
+ .help = "Unload program with id <id>"),
+ DEFINE_OPTION("all", OPT_BOOL, struct unloadopt, all,
+ .short_opt = 'a',
+ .help = "Unload all programs from interface"),
+ END_OPTIONS
+};
+
+int do_unload(const void *cfg, __unused const char *pin_root_path)
+{
+ const struct unloadopt *opt = cfg;
+ struct xdp_multiprog *mp = NULL;
+ enum xdp_attach_mode mode;
+ int err = EXIT_FAILURE;
+ DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .pin_root_path = pin_root_path);
+
+ if (!opt->all && !opt->prog_id) {
+ pr_warn("Need prog ID or --all\n");
+ goto out;
+ }
+
+ if (!opt->iface.ifindex) {
+ pr_warn("Must specify ifname\n");
+ goto out;
+ }
+
+ mp = xdp_multiprog__get_from_ifindex(opt->iface.ifindex);
+ if (IS_ERR_OR_NULL(mp)) {
+ pr_warn("No XDP program loaded on %s\n", opt->iface.ifname);
+ mp = NULL;
+ goto out;
+ }
+
+ if (opt->all) {
+ err = xdp_multiprog__detach(mp);
+ if (err) {
+ pr_warn("Unable to detach XDP program: %s\n",
+ strerror(-err));
+ goto out;
+ }
+ } else {
+ struct xdp_program *prog = NULL;
+
+ while ((prog = xdp_multiprog__next_prog(prog, mp))) {
+ if (xdp_program__id(prog) == opt->prog_id) {
+ mode = xdp_multiprog__attach_mode(mp);
+ goto found;
+ }
+ }
+
+ if (xdp_multiprog__is_legacy(mp)) {
+ prog = xdp_multiprog__main_prog(mp);
+ if (xdp_program__id(prog) == opt->prog_id) {
+ mode = xdp_multiprog__attach_mode(mp);
+ goto found;
+ }
+ }
+
+ prog = xdp_multiprog__hw_prog(mp);
+ if (xdp_program__id(prog) == opt->prog_id) {
+ mode = XDP_MODE_HW;
+ goto found;
+ }
+
+ pr_warn("Program with ID %u not loaded on %s\n",
+ opt->prog_id, opt->iface.ifname);
+ err = -ENOENT;
+ goto out;
+
+found:
+ pr_debug("Detaching XDP program with ID %u from %s\n",
+ xdp_program__id(prog), opt->iface.ifname);
+ err = xdp_program__detach(prog, opt->iface.ifindex, mode, 0);
+ if (err) {
+ pr_warn("Unable to detach XDP program: %s\n",
+ strerror(-err));
+ goto out;
+ }
+ }
+
+out:
+ xdp_multiprog__close(mp);
+ return err ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static const struct statusopt {
+ struct iface iface;
+} defaults_status = {};
+
+static struct prog_option status_options[] = {
+ DEFINE_OPTION("dev", OPT_IFNAME, struct statusopt, iface,
+ .positional = true, .metavar = "[ifname]",
+ .help = "Show status for device [ifname] (default all interfaces)"),
+ END_OPTIONS
+};
+
+int do_status(const void *cfg, __unused const char *pin_root_path)
+{
+ const struct statusopt *opt = cfg;
+
+ printf("CURRENT XDP PROGRAM STATUS:\n\n");
+ return iface_print_status(opt->iface.ifindex ? &opt->iface : NULL);
+}
+
+static const struct cleanopt {
+ struct iface iface;
+} defaults_clean = {};
+
+static struct prog_option clean_options[] = {
+ DEFINE_OPTION("dev", OPT_IFNAME, struct cleanopt, iface,
+ .positional = true, .metavar = "[ifname]",
+ .help = "Clean up detached program links for [ifname] (default all interfaces)"),
+ END_OPTIONS
+};
+
+int do_clean(const void *cfg, __unused const char *pin_root_path)
+{
+ const struct cleanopt *opt = cfg;
+
+ printf("Cleaning up detached XDP program links for %s\n", opt->iface.ifindex ?
+ opt->iface.ifname : "all interfaces");
+ return libxdp_clean_references(opt->iface.ifindex);
+}
+
+int do_help(__unused const void *cfg, __unused const char *pin_root_path)
+{
+ fprintf(stderr,
+ "Usage: xdp-loader COMMAND [options]\n"
+ "\n"
+ "COMMAND can be one of:\n"
+ " load - load an XDP program on an interface\n"
+ " unload - unload an XDP program from an interface\n"
+ " status - show current XDP program status\n"
+ " clean - clean up detached program links in XDP bpffs directory\n"
+ " help - show this help message\n"
+ "\n"
+ "Use 'xdp-loader COMMAND --help' to see options for each command\n");
+ return -1;
+}
+
+static const struct prog_command cmds[] = {
+ DEFINE_COMMAND(load, "Load an XDP program on an interface"),
+ DEFINE_COMMAND(unload, "Unload an XDP program from an interface"),
+ DEFINE_COMMAND(clean, "Clean up detached program links in XDP bpffs directory"),
+ DEFINE_COMMAND(status, "Show XDP program status"),
+ { .name = "help", .func = do_help, .no_cfg = true },
+ END_COMMANDS
+};
+
+union all_opts {
+ struct loadopt load;
+ struct unloadopt unload;
+ struct statusopt status;
+};
+
+int main(int argc, char **argv)
+{
+ if (argc > 1)
+ return dispatch_commands(argv[1], argc - 1, argv + 1, cmds,
+ sizeof(union all_opts), PROG_NAME, false);
+
+ return do_help(NULL, NULL);
+}