1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/tools/testing/selftests/bpf/xdp_synproxy.c
Daniel Müller afef88e655 selftests/bpf: Store BPF object files with .bpf.o extension
BPF object files are, in a way, the final artifact produced as part of
the ahead-of-time compilation process. That makes them somewhat special
compared to "regular" object files, which are a intermediate build
artifacts that can typically be removed safely. As such, it can make
sense to name them differently to make it easier to spot this difference
at a glance.

Among others, libbpf-bootstrap [0] has established the extension .bpf.o
for BPF object files. It seems reasonable to follow this example and
establish the same denomination for selftest build artifacts. To that
end, this change adjusts the corresponding part of the build system and
the test programs loading BPF object files to work with .bpf.o files.

  [0] https://github.com/libbpf/libbpf-bootstrap

Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Daniel Müller <deso@posteo.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20220901222253.1199242-1-deso@posteo.net
2022-09-02 15:55:37 +02:00

466 lines
11 KiB
C

// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include <stdnoreturn.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <sys/types.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <net/if.h>
#include <linux/if_link.h>
#include <linux/limits.h>
static unsigned int ifindex;
static __u32 attached_prog_id;
static bool attached_tc;
static void noreturn cleanup(int sig)
{
LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
int prog_fd;
int err;
if (attached_prog_id == 0)
exit(0);
if (attached_tc) {
LIBBPF_OPTS(bpf_tc_hook, hook,
.ifindex = ifindex,
.attach_point = BPF_TC_INGRESS);
err = bpf_tc_hook_destroy(&hook);
if (err < 0) {
fprintf(stderr, "Error: bpf_tc_hook_destroy: %s\n", strerror(-err));
fprintf(stderr, "Failed to destroy the TC hook\n");
exit(1);
}
exit(0);
}
prog_fd = bpf_prog_get_fd_by_id(attached_prog_id);
if (prog_fd < 0) {
fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd));
err = bpf_xdp_attach(ifindex, -1, 0, NULL);
if (err < 0) {
fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n", strerror(-err));
fprintf(stderr, "Failed to detach XDP program\n");
exit(1);
}
} else {
opts.old_prog_fd = prog_fd;
err = bpf_xdp_attach(ifindex, -1, XDP_FLAGS_REPLACE, &opts);
close(prog_fd);
if (err < 0) {
fprintf(stderr, "Error: bpf_set_link_xdp_fd_opts: %s\n", strerror(-err));
/* Not an error if already replaced by someone else. */
if (err != -EEXIST) {
fprintf(stderr, "Failed to detach XDP program\n");
exit(1);
}
}
}
exit(0);
}
static noreturn void usage(const char *progname)
{
fprintf(stderr, "Usage: %s [--iface <iface>|--prog <prog_id>] [--mss4 <mss ipv4> --mss6 <mss ipv6> --wscale <wscale> --ttl <ttl>] [--ports <port1>,<port2>,...] [--single] [--tc]\n",
progname);
exit(1);
}
static unsigned long parse_arg_ul(const char *progname, const char *arg, unsigned long limit)
{
unsigned long res;
char *endptr;
errno = 0;
res = strtoul(arg, &endptr, 10);
if (errno != 0 || *endptr != '\0' || arg[0] == '\0' || res > limit)
usage(progname);
return res;
}
static void parse_options(int argc, char *argv[], unsigned int *ifindex, __u32 *prog_id,
__u64 *tcpipopts, char **ports, bool *single, bool *tc)
{
static struct option long_options[] = {
{ "help", no_argument, NULL, 'h' },
{ "iface", required_argument, NULL, 'i' },
{ "prog", required_argument, NULL, 'x' },
{ "mss4", required_argument, NULL, 4 },
{ "mss6", required_argument, NULL, 6 },
{ "wscale", required_argument, NULL, 'w' },
{ "ttl", required_argument, NULL, 't' },
{ "ports", required_argument, NULL, 'p' },
{ "single", no_argument, NULL, 's' },
{ "tc", no_argument, NULL, 'c' },
{ NULL, 0, NULL, 0 },
};
unsigned long mss4, mss6, wscale, ttl;
unsigned int tcpipopts_mask = 0;
if (argc < 2)
usage(argv[0]);
*ifindex = 0;
*prog_id = 0;
*tcpipopts = 0;
*ports = NULL;
*single = false;
while (true) {
int opt;
opt = getopt_long(argc, argv, "", long_options, NULL);
if (opt == -1)
break;
switch (opt) {
case 'h':
usage(argv[0]);
break;
case 'i':
*ifindex = if_nametoindex(optarg);
if (*ifindex == 0)
usage(argv[0]);
break;
case 'x':
*prog_id = parse_arg_ul(argv[0], optarg, UINT32_MAX);
if (*prog_id == 0)
usage(argv[0]);
break;
case 4:
mss4 = parse_arg_ul(argv[0], optarg, UINT16_MAX);
tcpipopts_mask |= 1 << 0;
break;
case 6:
mss6 = parse_arg_ul(argv[0], optarg, UINT16_MAX);
tcpipopts_mask |= 1 << 1;
break;
case 'w':
wscale = parse_arg_ul(argv[0], optarg, 14);
tcpipopts_mask |= 1 << 2;
break;
case 't':
ttl = parse_arg_ul(argv[0], optarg, UINT8_MAX);
tcpipopts_mask |= 1 << 3;
break;
case 'p':
*ports = optarg;
break;
case 's':
*single = true;
break;
case 'c':
*tc = true;
break;
default:
usage(argv[0]);
}
}
if (optind < argc)
usage(argv[0]);
if (tcpipopts_mask == 0xf) {
if (mss4 == 0 || mss6 == 0 || wscale == 0 || ttl == 0)
usage(argv[0]);
*tcpipopts = (mss6 << 32) | (ttl << 24) | (wscale << 16) | mss4;
} else if (tcpipopts_mask != 0) {
usage(argv[0]);
}
if (*ifindex != 0 && *prog_id != 0)
usage(argv[0]);
if (*ifindex == 0 && *prog_id == 0)
usage(argv[0]);
}
static int syncookie_attach(const char *argv0, unsigned int ifindex, bool tc)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
char xdp_filename[PATH_MAX];
struct bpf_program *prog;
struct bpf_object *obj;
int prog_fd;
int err;
snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.bpf.o", argv0);
obj = bpf_object__open_file(xdp_filename, NULL);
err = libbpf_get_error(obj);
if (err < 0) {
fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err));
return err;
}
err = bpf_object__load(obj);
if (err < 0) {
fprintf(stderr, "Error: bpf_object__open_file: %s\n", strerror(-err));
return err;
}
prog = bpf_object__find_program_by_name(obj, tc ? "syncookie_tc" : "syncookie_xdp");
if (!prog) {
fprintf(stderr, "Error: bpf_object__find_program_by_name: program was not found\n");
return -ENOENT;
}
prog_fd = bpf_program__fd(prog);
err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
if (err < 0) {
fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err));
goto out;
}
attached_tc = tc;
attached_prog_id = info.id;
signal(SIGINT, cleanup);
signal(SIGTERM, cleanup);
if (tc) {
LIBBPF_OPTS(bpf_tc_hook, hook,
.ifindex = ifindex,
.attach_point = BPF_TC_INGRESS);
LIBBPF_OPTS(bpf_tc_opts, opts,
.handle = 1,
.priority = 1,
.prog_fd = prog_fd);
err = bpf_tc_hook_create(&hook);
if (err < 0) {
fprintf(stderr, "Error: bpf_tc_hook_create: %s\n",
strerror(-err));
goto fail;
}
err = bpf_tc_attach(&hook, &opts);
if (err < 0) {
fprintf(stderr, "Error: bpf_tc_attach: %s\n",
strerror(-err));
goto fail;
}
} else {
err = bpf_xdp_attach(ifindex, prog_fd,
XDP_FLAGS_UPDATE_IF_NOEXIST, NULL);
if (err < 0) {
fprintf(stderr, "Error: bpf_set_link_xdp_fd: %s\n",
strerror(-err));
goto fail;
}
}
err = 0;
out:
bpf_object__close(obj);
return err;
fail:
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
attached_prog_id = 0;
goto out;
}
static int syncookie_open_bpf_maps(__u32 prog_id, int *values_map_fd, int *ports_map_fd)
{
struct bpf_prog_info prog_info;
__u32 map_ids[8];
__u32 info_len;
int prog_fd;
int err;
int i;
*values_map_fd = -1;
*ports_map_fd = -1;
prog_fd = bpf_prog_get_fd_by_id(prog_id);
if (prog_fd < 0) {
fprintf(stderr, "Error: bpf_prog_get_fd_by_id: %s\n", strerror(-prog_fd));
return prog_fd;
}
prog_info = (struct bpf_prog_info) {
.nr_map_ids = 8,
.map_ids = (__u64)map_ids,
};
info_len = sizeof(prog_info);
err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
if (err != 0) {
fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err));
goto out;
}
if (prog_info.nr_map_ids < 2) {
fprintf(stderr, "Error: Found %u BPF maps, expected at least 2\n",
prog_info.nr_map_ids);
err = -ENOENT;
goto out;
}
for (i = 0; i < prog_info.nr_map_ids; i++) {
struct bpf_map_info map_info = {};
int map_fd;
err = bpf_map_get_fd_by_id(map_ids[i]);
if (err < 0) {
fprintf(stderr, "Error: bpf_map_get_fd_by_id: %s\n", strerror(-err));
goto err_close_map_fds;
}
map_fd = err;
info_len = sizeof(map_info);
err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
if (err != 0) {
fprintf(stderr, "Error: bpf_obj_get_info_by_fd: %s\n", strerror(-err));
close(map_fd);
goto err_close_map_fds;
}
if (strcmp(map_info.name, "values") == 0) {
*values_map_fd = map_fd;
continue;
}
if (strcmp(map_info.name, "allowed_ports") == 0) {
*ports_map_fd = map_fd;
continue;
}
close(map_fd);
}
if (*values_map_fd != -1 && *ports_map_fd != -1) {
err = 0;
goto out;
}
err = -ENOENT;
err_close_map_fds:
if (*values_map_fd != -1)
close(*values_map_fd);
if (*ports_map_fd != -1)
close(*ports_map_fd);
*values_map_fd = -1;
*ports_map_fd = -1;
out:
close(prog_fd);
return err;
}
int main(int argc, char *argv[])
{
int values_map_fd, ports_map_fd;
__u64 tcpipopts;
bool firstiter;
__u64 prevcnt;
__u32 prog_id;
char *ports;
bool single;
int err = 0;
bool tc;
parse_options(argc, argv, &ifindex, &prog_id, &tcpipopts, &ports,
&single, &tc);
if (prog_id == 0) {
if (!tc) {
err = bpf_xdp_query_id(ifindex, 0, &prog_id);
if (err < 0) {
fprintf(stderr, "Error: bpf_get_link_xdp_id: %s\n",
strerror(-err));
goto out;
}
}
if (prog_id == 0) {
err = syncookie_attach(argv[0], ifindex, tc);
if (err < 0)
goto out;
prog_id = attached_prog_id;
}
}
err = syncookie_open_bpf_maps(prog_id, &values_map_fd, &ports_map_fd);
if (err < 0)
goto out;
if (ports) {
__u16 port_last = 0;
__u32 port_idx = 0;
char *p = ports;
fprintf(stderr, "Replacing allowed ports\n");
while (p && *p != '\0') {
char *token = strsep(&p, ",");
__u16 port;
port = parse_arg_ul(argv[0], token, UINT16_MAX);
err = bpf_map_update_elem(ports_map_fd, &port_idx, &port, BPF_ANY);
if (err != 0) {
fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err));
fprintf(stderr, "Failed to add port %u (index %u)\n",
port, port_idx);
goto out_close_maps;
}
fprintf(stderr, "Added port %u\n", port);
port_idx++;
}
err = bpf_map_update_elem(ports_map_fd, &port_idx, &port_last, BPF_ANY);
if (err != 0) {
fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err));
fprintf(stderr, "Failed to add the terminator value 0 (index %u)\n",
port_idx);
goto out_close_maps;
}
}
if (tcpipopts) {
__u32 key = 0;
fprintf(stderr, "Replacing TCP/IP options\n");
err = bpf_map_update_elem(values_map_fd, &key, &tcpipopts, BPF_ANY);
if (err != 0) {
fprintf(stderr, "Error: bpf_map_update_elem: %s\n", strerror(-err));
goto out_close_maps;
}
}
if ((ports || tcpipopts) && attached_prog_id == 0 && !single)
goto out_close_maps;
prevcnt = 0;
firstiter = true;
while (true) {
__u32 key = 1;
__u64 value;
err = bpf_map_lookup_elem(values_map_fd, &key, &value);
if (err != 0) {
fprintf(stderr, "Error: bpf_map_lookup_elem: %s\n", strerror(-err));
goto out_close_maps;
}
if (firstiter) {
prevcnt = value;
firstiter = false;
}
if (single) {
printf("Total SYNACKs generated: %llu\n", value);
break;
}
printf("SYNACKs generated: %llu (total %llu)\n", value - prevcnt, value);
prevcnt = value;
sleep(1);
}
out_close_maps:
close(values_map_fd);
close(ports_map_fd);
out:
return err == 0 ? 0 : 1;
}