samples: bpf: Convert xdp_redirect to XDP samples helper
Use the libbpf skeleton facility and other utilities provided by XDP samples helper. One important note: The XDP samples helper handles ownership of installed XDP programs on devices, including responding to SIGINT and SIGTERM, so drop the code here and use the helpers we provide going forward for all xdp_redirect* conversions. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/20210821002010.845777-17-memxor@gmail.com
This commit is contained in:
parent
66fc4ca85d
commit
b926c55d85
@ -39,7 +39,6 @@ tprogs-y += lwt_len_hist
|
|||||||
tprogs-y += xdp_tx_iptunnel
|
tprogs-y += xdp_tx_iptunnel
|
||||||
tprogs-y += test_map_in_map
|
tprogs-y += test_map_in_map
|
||||||
tprogs-y += per_socket_stats_example
|
tprogs-y += per_socket_stats_example
|
||||||
tprogs-y += xdp_redirect
|
|
||||||
tprogs-y += xdp_redirect_map
|
tprogs-y += xdp_redirect_map
|
||||||
tprogs-y += xdp_redirect_map_multi
|
tprogs-y += xdp_redirect_map_multi
|
||||||
tprogs-y += xdp_redirect_cpu
|
tprogs-y += xdp_redirect_cpu
|
||||||
@ -56,6 +55,7 @@ tprogs-y += xdp_sample_pkts
|
|||||||
tprogs-y += ibumad
|
tprogs-y += ibumad
|
||||||
tprogs-y += hbm
|
tprogs-y += hbm
|
||||||
|
|
||||||
|
tprogs-y += xdp_redirect
|
||||||
tprogs-y += xdp_monitor
|
tprogs-y += xdp_monitor
|
||||||
|
|
||||||
# Libbpf dependencies
|
# Libbpf dependencies
|
||||||
@ -100,7 +100,6 @@ lwt_len_hist-objs := lwt_len_hist_user.o
|
|||||||
xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o
|
xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o
|
||||||
test_map_in_map-objs := test_map_in_map_user.o
|
test_map_in_map-objs := test_map_in_map_user.o
|
||||||
per_socket_stats_example-objs := cookie_uid_helper_example.o
|
per_socket_stats_example-objs := cookie_uid_helper_example.o
|
||||||
xdp_redirect-objs := xdp_redirect_user.o
|
|
||||||
xdp_redirect_map-objs := xdp_redirect_map_user.o
|
xdp_redirect_map-objs := xdp_redirect_map_user.o
|
||||||
xdp_redirect_map_multi-objs := xdp_redirect_map_multi_user.o
|
xdp_redirect_map_multi-objs := xdp_redirect_map_multi_user.o
|
||||||
xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o
|
xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o
|
||||||
@ -117,6 +116,7 @@ xdp_sample_pkts-objs := xdp_sample_pkts_user.o
|
|||||||
ibumad-objs := ibumad_user.o
|
ibumad-objs := ibumad_user.o
|
||||||
hbm-objs := hbm.o $(CGROUP_HELPERS)
|
hbm-objs := hbm.o $(CGROUP_HELPERS)
|
||||||
|
|
||||||
|
xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE)
|
||||||
xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
|
xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
|
||||||
|
|
||||||
# Tell kbuild to always build the programs
|
# Tell kbuild to always build the programs
|
||||||
@ -312,6 +312,7 @@ verify_target_bpf: verify_cmds
|
|||||||
$(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF)
|
$(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF)
|
||||||
$(src)/*.c: verify_target_bpf $(LIBBPF)
|
$(src)/*.c: verify_target_bpf $(LIBBPF)
|
||||||
|
|
||||||
|
$(obj)/xdp_redirect_user.o: $(obj)/xdp_redirect.skel.h
|
||||||
$(obj)/xdp_monitor_user.o: $(obj)/xdp_monitor.skel.h
|
$(obj)/xdp_monitor_user.o: $(obj)/xdp_monitor.skel.h
|
||||||
|
|
||||||
$(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
|
$(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
/* Copyright (c) 2016 John Fastabend <john.r.fastabend@intel.com>
|
/* Copyright (c) 2016 John Fastabend <john.r.fastabend@intel.com>
|
||||||
*/
|
*/
|
||||||
|
static const char *__doc__ =
|
||||||
|
"XDP redirect tool, using bpf_redirect helper\n"
|
||||||
|
"Usage: xdp_redirect <IFINDEX|IFNAME>_IN <IFINDEX|IFNAME>_OUT\n";
|
||||||
|
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include <linux/if_link.h>
|
#include <linux/if_link.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -13,126 +17,73 @@
|
|||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
#include "bpf_util.h"
|
|
||||||
#include <bpf/bpf.h>
|
#include <bpf/bpf.h>
|
||||||
#include <bpf/libbpf.h>
|
#include <bpf/libbpf.h>
|
||||||
|
#include "bpf_util.h"
|
||||||
|
#include "xdp_sample_user.h"
|
||||||
|
#include "xdp_redirect.skel.h"
|
||||||
|
|
||||||
static int ifindex_in;
|
static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
|
||||||
static int ifindex_out;
|
SAMPLE_EXCEPTION_CNT | SAMPLE_DEVMAP_XMIT_CNT_MULTI;
|
||||||
static bool ifindex_out_xdp_dummy_attached = true;
|
|
||||||
static __u32 prog_id;
|
|
||||||
static __u32 dummy_prog_id;
|
|
||||||
|
|
||||||
static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
DEFINE_SAMPLE_INIT(xdp_redirect);
|
||||||
static int rxcnt_map_fd;
|
|
||||||
|
|
||||||
static void int_exit(int sig)
|
|
||||||
{
|
|
||||||
__u32 curr_prog_id = 0;
|
|
||||||
|
|
||||||
if (bpf_get_link_xdp_id(ifindex_in, &curr_prog_id, xdp_flags)) {
|
|
||||||
printf("bpf_get_link_xdp_id failed\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (prog_id == curr_prog_id)
|
|
||||||
bpf_set_link_xdp_fd(ifindex_in, -1, xdp_flags);
|
|
||||||
else if (!curr_prog_id)
|
|
||||||
printf("couldn't find a prog id on iface IN\n");
|
|
||||||
else
|
|
||||||
printf("program on iface IN changed, not removing\n");
|
|
||||||
|
|
||||||
if (ifindex_out_xdp_dummy_attached) {
|
|
||||||
curr_prog_id = 0;
|
|
||||||
if (bpf_get_link_xdp_id(ifindex_out, &curr_prog_id,
|
|
||||||
xdp_flags)) {
|
|
||||||
printf("bpf_get_link_xdp_id failed\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (dummy_prog_id == curr_prog_id)
|
|
||||||
bpf_set_link_xdp_fd(ifindex_out, -1, xdp_flags);
|
|
||||||
else if (!curr_prog_id)
|
|
||||||
printf("couldn't find a prog id on iface OUT\n");
|
|
||||||
else
|
|
||||||
printf("program on iface OUT changed, not removing\n");
|
|
||||||
}
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void poll_stats(int interval, int ifindex)
|
|
||||||
{
|
|
||||||
unsigned int nr_cpus = bpf_num_possible_cpus();
|
|
||||||
__u64 values[nr_cpus], prev[nr_cpus];
|
|
||||||
|
|
||||||
memset(prev, 0, sizeof(prev));
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
__u64 sum = 0;
|
|
||||||
__u32 key = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
sleep(interval);
|
|
||||||
assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
|
|
||||||
for (i = 0; i < nr_cpus; i++)
|
|
||||||
sum += (values[i] - prev[i]);
|
|
||||||
if (sum)
|
|
||||||
printf("ifindex %i: %10llu pkt/s\n",
|
|
||||||
ifindex, sum / interval);
|
|
||||||
memcpy(prev, values, sizeof(values));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usage(const char *prog)
|
|
||||||
{
|
|
||||||
fprintf(stderr,
|
|
||||||
"usage: %s [OPTS] <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n\n"
|
|
||||||
"OPTS:\n"
|
|
||||||
" -S use skb-mode\n"
|
|
||||||
" -N enforce native mode\n"
|
|
||||||
" -F force loading prog\n",
|
|
||||||
prog);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
static const struct option long_options[] = {
|
||||||
|
{"help", no_argument, NULL, 'h' },
|
||||||
|
{"skb-mode", no_argument, NULL, 'S' },
|
||||||
|
{"force", no_argument, NULL, 'F' },
|
||||||
|
{"stats", no_argument, NULL, 's' },
|
||||||
|
{"interval", required_argument, NULL, 'i' },
|
||||||
|
{"verbose", no_argument, NULL, 'v' },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct bpf_prog_load_attr prog_load_attr = {
|
int ifindex_in, ifindex_out, opt;
|
||||||
.prog_type = BPF_PROG_TYPE_XDP,
|
char str[2 * IF_NAMESIZE + 1];
|
||||||
};
|
char ifname_out[IF_NAMESIZE];
|
||||||
struct bpf_program *prog, *dummy_prog;
|
char ifname_in[IF_NAMESIZE];
|
||||||
int prog_fd, tx_port_map_fd, opt;
|
int ret = EXIT_FAIL_OPTION;
|
||||||
struct bpf_prog_info info = {};
|
unsigned long interval = 2;
|
||||||
__u32 info_len = sizeof(info);
|
struct xdp_redirect *skel;
|
||||||
const char *optstr = "FSN";
|
bool generic = false;
|
||||||
struct bpf_object *obj;
|
bool force = false;
|
||||||
char filename[256];
|
bool error = true;
|
||||||
int dummy_prog_fd;
|
|
||||||
int ret, key = 0;
|
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, optstr)) != -1) {
|
while ((opt = getopt_long(argc, argv, "hSFi:vs",
|
||||||
|
long_options, NULL)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'S':
|
case 'S':
|
||||||
xdp_flags |= XDP_FLAGS_SKB_MODE;
|
generic = true;
|
||||||
break;
|
mask &= ~(SAMPLE_DEVMAP_XMIT_CNT |
|
||||||
case 'N':
|
SAMPLE_DEVMAP_XMIT_CNT_MULTI);
|
||||||
/* default, set below */
|
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
|
force = true;
|
||||||
break;
|
break;
|
||||||
|
case 'i':
|
||||||
|
interval = strtoul(optarg, NULL, 0);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
sample_switch_mode();
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
mask |= SAMPLE_REDIRECT_CNT;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
error = false;
|
||||||
default:
|
default:
|
||||||
usage(basename(argv[0]));
|
sample_usage(argv, long_options, __doc__, mask, error);
|
||||||
return 1;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
|
if (argc <= optind + 1) {
|
||||||
xdp_flags |= XDP_FLAGS_DRV_MODE;
|
sample_usage(argv, long_options, __doc__, mask, true);
|
||||||
|
return ret;
|
||||||
if (optind + 2 != argc) {
|
|
||||||
printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ifindex_in = if_nametoindex(argv[optind]);
|
ifindex_in = if_nametoindex(argv[optind]);
|
||||||
@ -143,75 +94,80 @@ int main(int argc, char **argv)
|
|||||||
if (!ifindex_out)
|
if (!ifindex_out)
|
||||||
ifindex_out = strtoul(argv[optind + 1], NULL, 0);
|
ifindex_out = strtoul(argv[optind + 1], NULL, 0);
|
||||||
|
|
||||||
printf("input: %d output: %d\n", ifindex_in, ifindex_out);
|
if (!ifindex_in || !ifindex_out) {
|
||||||
|
fprintf(stderr, "Bad interface index or name\n");
|
||||||
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
|
sample_usage(argv, long_options, __doc__, mask, true);
|
||||||
prog_load_attr.file = filename;
|
goto end;
|
||||||
|
|
||||||
if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
prog = bpf_program__next(NULL, obj);
|
|
||||||
dummy_prog = bpf_program__next(prog, obj);
|
|
||||||
if (!prog || !dummy_prog) {
|
|
||||||
printf("finding a prog in obj file failed\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
|
|
||||||
* so we're missing only the fd for dummy prog
|
|
||||||
*/
|
|
||||||
dummy_prog_fd = bpf_program__fd(dummy_prog);
|
|
||||||
if (prog_fd < 0 || dummy_prog_fd < 0) {
|
|
||||||
printf("bpf_prog_load_xattr: %s\n", strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
|
skel = xdp_redirect__open();
|
||||||
rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
|
if (!skel) {
|
||||||
if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
|
fprintf(stderr, "Failed to xdp_redirect__open: %s\n", strerror(errno));
|
||||||
printf("bpf_object__find_map_fd_by_name failed\n");
|
ret = EXIT_FAIL_BPF;
|
||||||
return 1;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bpf_set_link_xdp_fd(ifindex_in, prog_fd, xdp_flags) < 0) {
|
ret = sample_init_pre_load(skel);
|
||||||
printf("ERROR: link set xdp fd failed on %d\n", ifindex_in);
|
if (ret < 0) {
|
||||||
return 1;
|
fprintf(stderr, "Failed to sample_init_pre_load: %s\n", strerror(-ret));
|
||||||
|
ret = EXIT_FAIL_BPF;
|
||||||
|
goto end_destroy;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
|
skel->rodata->from_match[0] = ifindex_in;
|
||||||
if (ret) {
|
skel->rodata->to_match[0] = ifindex_out;
|
||||||
printf("can't get prog info - %s\n", strerror(errno));
|
skel->rodata->ifindex_out = ifindex_out;
|
||||||
return ret;
|
|
||||||
|
ret = xdp_redirect__load(skel);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Failed to xdp_redirect__load: %s\n", strerror(errno));
|
||||||
|
ret = EXIT_FAIL_BPF;
|
||||||
|
goto end_destroy;
|
||||||
}
|
}
|
||||||
prog_id = info.id;
|
|
||||||
|
ret = sample_init(skel, mask);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret));
|
||||||
|
ret = EXIT_FAIL;
|
||||||
|
goto end_destroy;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = EXIT_FAIL_XDP;
|
||||||
|
if (sample_install_xdp(skel->progs.xdp_redirect_prog, ifindex_in,
|
||||||
|
generic, force) < 0)
|
||||||
|
goto end_destroy;
|
||||||
|
|
||||||
/* Loading dummy XDP prog on out-device */
|
/* Loading dummy XDP prog on out-device */
|
||||||
if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
|
sample_install_xdp(skel->progs.xdp_redirect_dummy_prog, ifindex_out,
|
||||||
(xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
|
generic, force);
|
||||||
printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
|
|
||||||
ifindex_out_xdp_dummy_attached = false;
|
ret = EXIT_FAIL;
|
||||||
|
if (!if_indextoname(ifindex_in, ifname_in)) {
|
||||||
|
fprintf(stderr, "Failed to if_indextoname for %d: %s\n", ifindex_in,
|
||||||
|
strerror(errno));
|
||||||
|
goto end_destroy;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
if (!if_indextoname(ifindex_out, ifname_out)) {
|
||||||
ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
|
fprintf(stderr, "Failed to if_indextoname for %d: %s\n", ifindex_out,
|
||||||
if (ret) {
|
strerror(errno));
|
||||||
printf("can't get prog info - %s\n", strerror(errno));
|
goto end_destroy;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
dummy_prog_id = info.id;
|
|
||||||
|
|
||||||
signal(SIGINT, int_exit);
|
|
||||||
signal(SIGTERM, int_exit);
|
|
||||||
|
|
||||||
/* bpf redirect port */
|
|
||||||
ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
|
|
||||||
if (ret) {
|
|
||||||
perror("bpf_update_elem");
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
poll_stats(2, ifindex_out);
|
safe_strncpy(str, get_driver_name(ifindex_in), sizeof(str));
|
||||||
|
printf("Redirecting from %s (ifindex %d; driver %s) to %s (ifindex %d; driver %s)\n",
|
||||||
|
ifname_in, ifindex_in, str, ifname_out, ifindex_out, get_driver_name(ifindex_out));
|
||||||
|
snprintf(str, sizeof(str), "%s->%s", ifname_in, ifname_out);
|
||||||
|
|
||||||
out:
|
ret = sample_run(interval, NULL, NULL);
|
||||||
return ret;
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret));
|
||||||
|
ret = EXIT_FAIL;
|
||||||
|
goto end_destroy;
|
||||||
|
}
|
||||||
|
ret = EXIT_OK;
|
||||||
|
end_destroy:
|
||||||
|
xdp_redirect__destroy(skel);
|
||||||
|
end:
|
||||||
|
sample_exit(ret);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user