mirror of
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git
synced 2024-11-16 14:35:34 +08:00
91d88eeb10
Since we have all infrastructure in place now, allow atomic live updates on program arrays. This can be very useful e.g. in case programs that are being tail-called need to be replaced, f.e. when classifier functionality needs to be changed, new protocols added/removed during runtime, etc. Thus, provide a way for in-place code updates, minimal example: Given is an object file cls.o that contains the entry point in section 'classifier', has a globally pinned program array 'jmp' with 2 slots and id of 0, and two tail called programs under section '0/0' (prog array key 0) and '0/1' (prog array key 1), the section encoding for the loader is <id/key>. Adding the filter loads everything into cls_bpf: tc filter add dev foo parent ffff: bpf da obj cls.o Now, the program under section '0/1' needs to be replaced with an updated version that resides in the same section (also full path to tc's subfolder of the mount point can be passed, e.g. /sys/fs/bpf/tc/globals/jmp): tc exec bpf graft m:globals/jmp obj cls.o sec 0/1 In case the program resides under a different section 'foo', it can also be injected into the program array like: tc exec bpf graft m:globals/jmp key 1 obj cls.o sec foo If the new tail called classifier program is already available as a pinned object somewhere (here: /sys/fs/bpf/tc/progs/parser), it can be injected into the prog array like: tc exec bpf graft m:globals/jmp key 1 fd m:progs/parser In the kernel, the program on key 1 is being atomically replaced and the old one's refcount dropped. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org>
180 lines
4.3 KiB
C
180 lines
4.3 KiB
C
/*
|
|
* e_bpf.c BPF exec proxy
|
|
*
|
|
* This program is free software; you can distribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
* Authors: Daniel Borkmann <daniel@iogearbox.net>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include "utils.h"
|
|
|
|
#include "tc_util.h"
|
|
#include "tc_bpf.h"
|
|
|
|
#include "bpf_elf.h"
|
|
#include "bpf_scm.h"
|
|
|
|
#define BPF_DEFAULT_CMD "/bin/sh"
|
|
|
|
static char *argv_default[] = { BPF_DEFAULT_CMD, NULL };
|
|
|
|
static void explain(void)
|
|
{
|
|
fprintf(stderr, "Usage: ... bpf [ import UDS_FILE ] [ run CMD ]\n");
|
|
fprintf(stderr, " ... bpf [ debug ]\n");
|
|
fprintf(stderr, " ... bpf [ graft MAP_FILE ] [ key KEY ]\n");
|
|
fprintf(stderr, " `... [ object-file OBJ_FILE ] [ type TYPE ] [ section NAME ] [ verbose ]\n");
|
|
fprintf(stderr, " `... [ object-pinned PROG_FILE ]\n");
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, "Where UDS_FILE provides the name of a unix domain socket file\n");
|
|
fprintf(stderr, "to import eBPF maps and the optional CMD denotes the command\n");
|
|
fprintf(stderr, "to be executed (default: \'%s\').\n", BPF_DEFAULT_CMD);
|
|
fprintf(stderr, "Where MAP_FILE points to a pinned map, OBJ_FILE to an object file\n");
|
|
fprintf(stderr, "and PROG_FILE to a pinned program. TYPE can be {cls, act}, where\n");
|
|
fprintf(stderr, "\'cls\' is default. KEY is optional and can be inferred from the\n");
|
|
fprintf(stderr, "section name, otherwise it needs to be provided.\n");
|
|
}
|
|
|
|
static int bpf_num_env_entries(void)
|
|
{
|
|
char **envp;
|
|
int num;
|
|
|
|
for (num = 0, envp = environ; *envp != NULL; envp++)
|
|
num++;
|
|
return num;
|
|
}
|
|
|
|
static int parse_bpf(struct exec_util *eu, int argc, char **argv)
|
|
{
|
|
char **argv_run = argv_default, **envp_run, *tmp;
|
|
int ret, i, env_old, env_num, env_map;
|
|
const char *bpf_uds_name = NULL;
|
|
int fds[BPF_SCM_MAX_FDS];
|
|
struct bpf_map_aux aux;
|
|
|
|
if (argc == 0)
|
|
return 0;
|
|
|
|
while (argc > 0) {
|
|
if (matches(*argv, "run") == 0) {
|
|
NEXT_ARG();
|
|
argv_run = argv;
|
|
break;
|
|
} else if (matches(*argv, "import") == 0) {
|
|
NEXT_ARG();
|
|
bpf_uds_name = *argv;
|
|
} else if (matches(*argv, "debug") == 0 ||
|
|
matches(*argv, "dbg") == 0) {
|
|
if (bpf_trace_pipe())
|
|
fprintf(stderr,
|
|
"No trace pipe, tracefs not mounted?\n");
|
|
return -1;
|
|
} else if (matches(*argv, "graft") == 0) {
|
|
const char *bpf_map_path;
|
|
bool has_key = false;
|
|
uint32_t key;
|
|
|
|
NEXT_ARG();
|
|
bpf_map_path = *argv;
|
|
NEXT_ARG();
|
|
if (matches(*argv, "key") == 0) {
|
|
NEXT_ARG();
|
|
if (get_unsigned(&key, *argv, 0)) {
|
|
fprintf(stderr, "Illegal \"key\"\n");
|
|
return -1;
|
|
}
|
|
has_key = true;
|
|
NEXT_ARG();
|
|
}
|
|
return bpf_graft_map(bpf_map_path, has_key ?
|
|
&key : NULL, argc, argv);
|
|
} else {
|
|
explain();
|
|
return -1;
|
|
}
|
|
|
|
NEXT_ARG_FWD();
|
|
}
|
|
|
|
if (!bpf_uds_name) {
|
|
fprintf(stderr, "bpf: No import parameter provided!\n");
|
|
explain();
|
|
return -1;
|
|
}
|
|
|
|
if (argv_run != argv_default && argc == 0) {
|
|
fprintf(stderr, "bpf: No run command provided!\n");
|
|
explain();
|
|
return -1;
|
|
}
|
|
|
|
memset(fds, 0, sizeof(fds));
|
|
memset(&aux, 0, sizeof(aux));
|
|
|
|
ret = bpf_recv_map_fds(bpf_uds_name, fds, &aux, ARRAY_SIZE(fds));
|
|
if (ret < 0) {
|
|
fprintf(stderr, "bpf: Could not receive fds!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (aux.num_ent == 0) {
|
|
envp_run = environ;
|
|
goto out;
|
|
}
|
|
|
|
env_old = bpf_num_env_entries();
|
|
env_num = env_old + aux.num_ent + 2;
|
|
env_map = env_old + 1;
|
|
|
|
envp_run = malloc(sizeof(*envp_run) * env_num);
|
|
if (!envp_run) {
|
|
fprintf(stderr, "bpf: No memory left to allocate env!\n");
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < env_old; i++)
|
|
envp_run[i] = environ[i];
|
|
|
|
ret = asprintf(&tmp, "BPF_NUM_MAPS=%u", aux.num_ent);
|
|
if (ret < 0)
|
|
goto err_free;
|
|
|
|
envp_run[env_old] = tmp;
|
|
|
|
for (i = env_map; i < env_num - 1; i++) {
|
|
ret = asprintf(&tmp, "BPF_MAP%u=%u",
|
|
aux.ent[i - env_map].id,
|
|
fds[i - env_map]);
|
|
if (ret < 0)
|
|
goto err_free_env;
|
|
|
|
envp_run[i] = tmp;
|
|
}
|
|
|
|
envp_run[env_num - 1] = NULL;
|
|
out:
|
|
return execvpe(argv_run[0], argv_run, envp_run);
|
|
|
|
err_free_env:
|
|
for (--i; i >= env_old; i--)
|
|
free(envp_run[i]);
|
|
err_free:
|
|
free(envp_run);
|
|
err:
|
|
for (i = 0; i < aux.num_ent; i++)
|
|
close(fds[i]);
|
|
return -1;
|
|
}
|
|
|
|
struct exec_util bpf_exec_util = {
|
|
.id = "bpf",
|
|
.parse_eopt = parse_bpf,
|
|
};
|