Skip to content

Commit

Permalink
Add --use-libxdp option and config, load libxdp dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
hack3ric committed Dec 7, 2024
1 parent 3e2c01f commit 4df319c
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ endif
ifneq ($(USE_LIBXDP),)
BPF_CFLAGS += -DMIMIC_USE_LIBXDP
CFLAGS += -DMIMIC_USE_LIBXDP
mimic_link_libs += -lxdp
# mimic_link_libs += -lxdp
endif

# Whether to use argp-standalone
Expand Down
6 changes: 5 additions & 1 deletion docs/mimic.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
`-x, --xdp-mode`
: Force XDP attach mode, either skb or native

`-X, --use-libxdp`
: Use libxdp Use libxdp instead of libbpf to load XDP program.
: libxdp supports chaining multiple XDP programs on one interface. Mimic loads libxdp dynamically using dlopen.

`-h, --handshake`
: Controls retry behaviour of initiating connection (see [**CONFIGURATION/Handshake and Keepalive Parameters**](#handshake-and-keepalive-parameters))

Expand Down Expand Up @@ -114,7 +118,7 @@ See **/usr/share/doc/mimic/eth0.conf.example** for detailed examples.
`log.verbosity`
: Controls how much information should be printed. Log level equal to or higher (in number) than log verbosity will be discarded. Both number and string matching log levels are accepted. Number must be greater than or equal to 0. Defaults to info (2). Available levels are: 0 - error (cannot be discarded), 1 - warn, 2 - info, 3 - debug, 4 - trace.

`handshake`, `keepalive`, `padding`, `max_window`
`xdp_mode`, `use_libxdp`, `handshake`, `keepalive`, `padding`, `max_window`
: See [**OPTIONS**](#options).

`filter`
Expand Down
5 changes: 5 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ int parse_config_file(FILE* file, struct run_args* args) {
} else if (strcmp(k, "xdp_mode") == 0) {
args->xdp_mode = try(parse_xdp_mode(v));

} else if (strcmp(k, "use_libxdp") == 0) {
__s16 result = 0;
try(parse_bool(v, &result));
args->use_libxdp = result;

} else if (!try(parse_setting(k, v, &args->gsettings))) {
ret(-EINVAL, _("unknown key '%s'"), k);
}
Expand Down
64 changes: 64 additions & 0 deletions src/libxdp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <dlfcn.h>
#include <errno.h>
#include <xdp/libxdp.h>

#include "common/try.h"
#include "libxdp.h"

static void *libxdp_dl = NULL;

DLSYM_PROTOTYPE(libxdp_set_print) = NULL;
DLSYM_PROTOTYPE(xdp_program__from_bpf_obj) = NULL;
DLSYM_PROTOTYPE(xdp_program__attach) = NULL;
DLSYM_PROTOTYPE(xdp_program__detach) = NULL;
DLSYM_PROTOTYPE(xdp_program__close) = NULL;

static int dlsym_many_or_warnv(void *dl, va_list ap) {
void (**fn)(void);

while ((fn = va_arg(ap, typeof(fn)))) {
void (*tfn)(void);
const char *symbol;

symbol = va_arg(ap, typeof(symbol));

tfn = (typeof(tfn))dlsym(dl, symbol);
if (!tfn) ret(-ELIBBAD, "can't find symbol '%s': %s", symbol, dlerror());
*fn = tfn;
}

return 0;
}

static int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, ...) {
void *dl = NULL;
int retcode;

if (*dlp) return 0;

dl = dlopen(filename, RTLD_NOW | RTLD_NODELETE);
if (!dl) ret(-EOPNOTSUPP, "%s is not installed: %s", filename, dlerror());

log_debug("loaded '%s' via dlopen()", filename);

va_list ap;
va_start(ap, filename);
retcode = dlsym_many_or_warnv(dl, ap);
va_end(ap);
if (retcode < 0) goto cleanup;

*dlp = dl;
return 1;
cleanup:
if (dl) dlclose(dl);
return retcode;
}

#define dlopen_many_sym_or_warn(dlp, filename, ...) \
dlopen_many_sym_or_warn_sentinel(dlp, filename, __VA_ARGS__, NULL)

int dlopen_libxdp() {
return dlopen_many_sym_or_warn(
&libxdp_dl, "libxdp.so.1", DLSYM_ARG(libxdp_set_print), DLSYM_ARG(xdp_program__from_bpf_obj),
DLSYM_ARG(xdp_program__attach), DLSYM_ARG(xdp_program__detach), DLSYM_ARG(xdp_program__close));
}
24 changes: 24 additions & 0 deletions src/libxdp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef MIMIC_LIBXDP_H
#define MIMIC_LIBXDP_H

#include <assert.h>
#include <xdp/libxdp.h>

#define DLSYM_PROTOTYPE(symbol) typeof(symbol)* sym_##symbol

#define DLSYM_ARG(arg) \
({ \
assert(__builtin_types_compatible_p(typeof(sym_##arg), typeof(&arg))); \
&sym_##arg; \
}), \
#arg

extern DLSYM_PROTOTYPE(libxdp_set_print);
extern DLSYM_PROTOTYPE(xdp_program__from_bpf_obj);
extern DLSYM_PROTOTYPE(xdp_program__attach);
extern DLSYM_PROTOTYPE(xdp_program__detach);
extern DLSYM_PROTOTYPE(xdp_program__close);

int dlopen_libxdp();

#endif
3 changes: 3 additions & 0 deletions src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ void log_destroy(enum log_level level, struct conn_tuple* conn, enum destroy_typ
log_conn(level, conn, _("connection destroyed (%s)"), reason);
}

// TODO: filter other messages like:
// - turn 'libbpf: elf: skipping unrecognized data section' into Trace
// - turn 'libxdp: Error attaching XDP program ...' and 'XDP mode not supported; try using SKB mode' into Warn
int libbpf_print_fn(enum libbpf_print_level bpf_level, const char* format, va_list args) {
int ret = 0;
if (bpf_level == LIBBPF_WARN && LOG_ALLOW_WARN) {
Expand Down
1 change: 1 addition & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct args {
struct filter_settings gsettings;
unsigned int filter_count;
int xdp_mode;
bool use_libxdp;
} run;
struct show_args {
const char* ifname;
Expand Down
70 changes: 44 additions & 26 deletions src/run.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include <unistd.h>

#ifdef MIMIC_USE_LIBXDP
#include <xdp/libxdp.h>
#include "libxdp.h"
#endif

#include "bpf_skel.h"
Expand All @@ -44,6 +44,9 @@ static const struct argp_option options[] = {
{"filter", 'f', N_("FILTER"), 0,
N_("Specify what packets to process. This may be specified for multiple times."), 1},
{"xdp-mode", 'x', N_("MODE"), 0, N_("Force XDP attach mode, either skb or native"), 1},
#ifdef MIMIC_USE_LIBXDP
{"use-libxdp", 'X', NULL, 0, N_("Use libxdp instead of libbpf to load XDP program"), 1},
#endif
{"handshake", 'h', N_("i:r"), 0, N_("Controls retry behaviour of initiating connection"), 2},
{"keepalive", 'k', N_("t:i:r:s"), 0, N_("Controls keepalive mechanism"), 2},
{"padding", 'p', N_("bytes"), 0,
Expand Down Expand Up @@ -78,6 +81,11 @@ static inline error_t args_parse_opt(int key, char* arg, struct argp_state* stat
case 'x':
args->xdp_mode = try(parse_xdp_mode(arg));
break;
#ifdef MIMIC_USE_LIBXDP
case 'X':
args->use_libxdp = true;
break;
#endif
case 'h':
try(parse_handshake(arg, &args->gsettings.handshake));
break;
Expand Down Expand Up @@ -523,31 +531,35 @@ static inline int run_bpf(struct run_args* args, int lock_fd, const char* ifname

// XDP
#ifdef MIMIC_USE_LIBXDP
xdp_ingress = try2_p(xdp_program__from_bpf_obj(skel->obj, "xdp.frags"),
_("failed to create XDP program: %s"), strret);
// libxdp loads the BPF object when attaching.
retcode = xdp_program__attach(xdp_ingress, ifindex, xdp_mode, 0);
if (retcode < 0) {
log_error(_("failed to attach XDP program: %s"), strerror(-retcode));
if (args->use_libxdp) {
xdp_ingress = try2_p(sym_xdp_program__from_bpf_obj(skel->obj, "xdp.frags"),
_("failed to create XDP program: %s"), strret);
// libxdp loads the BPF object when attaching.
retcode = sym_xdp_program__attach(xdp_ingress, ifindex, xdp_mode, 0);
if (retcode < 0) {
log_error(_("failed to attach XDP program: %s"), strerror(-retcode));
#ifdef MIMIC_CHECKSUM_HACK_kfunc
if (retcode == -EINVAL && !is_kmod_loaded())
log_error(_("hint: did you load the Mimic kernel module?"));
if (retcode == -EINVAL && !is_kmod_loaded())
log_error(_("hint: did you load the Mimic kernel module?"));
#endif
cleanup(retcode);
}
#else
retcode = mimic_bpf__load(skel);
if (retcode < 0) {
log_error(_("failed to load BPF program: %s"), strerror(-retcode));
cleanup(retcode);
}
} else
#endif
{
retcode = mimic_bpf__load(skel);
if (retcode < 0) {
log_error(_("failed to load BPF program: %s"), strerror(-retcode));
#ifdef MIMIC_CHECKSUM_HACK_kfunc
if (retcode == -EINVAL && !is_kmod_loaded())
log_error(_("hint: did you load the Mimic kernel module?"));
if (retcode == -EINVAL && !is_kmod_loaded())
log_error(_("hint: did you load the Mimic kernel module?"));
#endif
cleanup(retcode);
cleanup(retcode);
}
try2(
bpf_xdp_attach(ifindex, bpf_program__fd(skel->progs.ingress_handler), args->xdp_mode, NULL),
_("failed to attach XDP program: %s"), strret);
}
try2(bpf_xdp_attach(ifindex, bpf_program__fd(skel->progs.ingress_handler), args->xdp_mode, NULL),
_("failed to attach XDP program: %s"), strret);
#endif
xdp_attached = true;

// TC
Expand Down Expand Up @@ -667,11 +679,14 @@ static inline int run_bpf(struct run_args* args, int lock_fd, const char* ifname
sigprocmask(SIG_SETMASK, NULL, NULL);
if (tc_hook_created) tc_hook_cleanup(&tc_hook_egress, &tc_opts_egress);
#ifdef MIMIC_USE_LIBXDP
if (xdp_attached) xdp_program__detach(xdp_ingress, ifindex, xdp_mode, 0);
xdp_program__close(xdp_ingress);
#else
if (xdp_attached) bpf_xdp_detach(ifindex, args->xdp_mode, NULL);
if (args->use_libxdp) {
if (xdp_attached) sym_xdp_program__detach(xdp_ingress, ifindex, xdp_mode, 0);
sym_xdp_program__close(xdp_ingress);
} else
#endif
{
if (xdp_attached) bpf_xdp_detach(ifindex, args->xdp_mode, NULL);
}
if (rb) ring_buffer__free(rb);
if (closure) ffi_closure_free(closure);
if (skel) mimic_bpf__destroy(skel);
Expand Down Expand Up @@ -750,7 +765,10 @@ int subcmd_run(struct run_args* args) {

libbpf_set_print(libbpf_print_fn);
#ifdef MIMIC_USE_LIBXDP
libxdp_set_print((libxdp_print_fn_t)libbpf_print_fn);
if (args->use_libxdp) {
dlopen_libxdp();
sym_libxdp_set_print((libxdp_print_fn_t)libbpf_print_fn);
}
#endif
retcode = run_bpf(args, lock_fd, args->ifname, ifindex);
close(lock_fd);
Expand Down

0 comments on commit 4df319c

Please sign in to comment.