mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-10 14:43:54 +08:00
selftests/bpf: Add tests for attaching bpf_link to netns
Extend the existing test case for flow dissector attaching to cover: - link creation, - link updates, - link info querying, - mixing links with direct prog attachment. Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/20200531082846.2117903-10-jakub@cloudflare.com
This commit is contained in:
parent
e948947a6e
commit
1f043f87bb
@ -11,6 +11,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
@ -18,21 +19,30 @@
|
||||
|
||||
#include "test_progs.h"
|
||||
|
||||
static bool is_attached(int netns)
|
||||
static int init_net = -1;
|
||||
|
||||
static __u32 query_attached_prog_id(int netns)
|
||||
{
|
||||
__u32 cnt;
|
||||
__u32 prog_ids[1] = {};
|
||||
__u32 prog_cnt = ARRAY_SIZE(prog_ids);
|
||||
int err;
|
||||
|
||||
err = bpf_prog_query(netns, BPF_FLOW_DISSECTOR, 0, NULL, NULL, &cnt);
|
||||
err = bpf_prog_query(netns, BPF_FLOW_DISSECTOR, 0, NULL,
|
||||
prog_ids, &prog_cnt);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_query");
|
||||
return true; /* fail-safe */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cnt > 0;
|
||||
return prog_cnt == 1 ? prog_ids[0] : 0;
|
||||
}
|
||||
|
||||
static int load_prog(void)
|
||||
static bool prog_is_attached(int netns)
|
||||
{
|
||||
return query_attached_prog_id(netns) > 0;
|
||||
}
|
||||
|
||||
static int load_prog(enum bpf_prog_type type)
|
||||
{
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, BPF_OK),
|
||||
@ -40,61 +50,566 @@ static int load_prog(void)
|
||||
};
|
||||
int fd;
|
||||
|
||||
fd = bpf_load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, prog,
|
||||
ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
|
||||
fd = bpf_load_program(type, prog, ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
|
||||
if (CHECK_FAIL(fd < 0))
|
||||
perror("bpf_load_program");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void do_flow_dissector_reattach(void)
|
||||
static __u32 query_prog_id(int prog)
|
||||
{
|
||||
int prog_fd[2] = { -1, -1 };
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
int err;
|
||||
|
||||
prog_fd[0] = load_prog();
|
||||
if (prog_fd[0] < 0)
|
||||
return;
|
||||
|
||||
prog_fd[1] = load_prog();
|
||||
if (prog_fd[1] < 0)
|
||||
goto out_close;
|
||||
|
||||
err = bpf_prog_attach(prog_fd[0], 0, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_attach-0");
|
||||
goto out_close;
|
||||
err = bpf_obj_get_info_by_fd(prog, &info, &info_len);
|
||||
if (CHECK_FAIL(err || info_len != sizeof(info))) {
|
||||
perror("bpf_obj_get_info_by_fd");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return info.id;
|
||||
}
|
||||
|
||||
static int unshare_net(int old_net)
|
||||
{
|
||||
int err, new_net;
|
||||
|
||||
err = unshare(CLONE_NEWNET);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("unshare(CLONE_NEWNET)");
|
||||
return -1;
|
||||
}
|
||||
new_net = open("/proc/self/ns/net", O_RDONLY);
|
||||
if (CHECK_FAIL(new_net < 0)) {
|
||||
perror("open(/proc/self/ns/net)");
|
||||
setns(old_net, CLONE_NEWNET);
|
||||
return -1;
|
||||
}
|
||||
return new_net;
|
||||
}
|
||||
|
||||
static void test_prog_attach_prog_attach(int netns, int prog1, int prog2)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bpf_prog_attach(prog1, 0, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_attach(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect success when attaching a different program */
|
||||
err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0);
|
||||
err = bpf_prog_attach(prog2, 0, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_attach-1");
|
||||
perror("bpf_prog_attach(prog2) #1");
|
||||
goto out_detach;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));
|
||||
|
||||
/* Expect failure when attaching the same program twice */
|
||||
err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0);
|
||||
err = bpf_prog_attach(prog2, 0, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(!err || errno != EINVAL))
|
||||
perror("bpf_prog_attach-2");
|
||||
perror("bpf_prog_attach(prog2) #2");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));
|
||||
|
||||
out_detach:
|
||||
err = bpf_prog_detach(0, BPF_FLOW_DISSECTOR);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("bpf_prog_detach");
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_create_link_create(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
|
||||
int link1, link2;
|
||||
|
||||
link1 = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect failure creating link when another link exists */
|
||||
errno = 0;
|
||||
link2 = bpf_link_create(prog2, netns, BPF_FLOW_DISSECTOR, &opts);
|
||||
if (CHECK_FAIL(link2 != -1 || errno != E2BIG))
|
||||
perror("bpf_prog_attach(prog2) expected E2BIG");
|
||||
if (link2 != -1)
|
||||
close(link2);
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
close(link1);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_prog_attach_link_create(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
|
||||
int err, link;
|
||||
|
||||
err = bpf_prog_attach(prog1, -1, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_attach(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect failure creating link when prog attached */
|
||||
errno = 0;
|
||||
link = bpf_link_create(prog2, netns, BPF_FLOW_DISSECTOR, &opts);
|
||||
if (CHECK_FAIL(link != -1 || errno != EEXIST))
|
||||
perror("bpf_link_create(prog2) expected EEXIST");
|
||||
if (link != -1)
|
||||
close(link);
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
err = bpf_prog_detach(-1, BPF_FLOW_DISSECTOR);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("bpf_prog_detach");
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_create_prog_attach(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
|
||||
int err, link;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect failure attaching prog when link exists */
|
||||
errno = 0;
|
||||
err = bpf_prog_attach(prog2, -1, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(!err || errno != EEXIST))
|
||||
perror("bpf_prog_attach(prog2) expected EEXIST");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
close(link);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_create_prog_detach(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
|
||||
int err, link;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect failure detaching prog when link exists */
|
||||
errno = 0;
|
||||
err = bpf_prog_detach(-1, BPF_FLOW_DISSECTOR);
|
||||
if (CHECK_FAIL(!err || errno != EINVAL))
|
||||
perror("bpf_prog_detach expected EINVAL");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
close(link);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_prog_attach_detach_query(int netns, int prog1, int prog2)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bpf_prog_attach(prog1, 0, BPF_FLOW_DISSECTOR, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_attach(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
err = bpf_prog_detach(0, BPF_FLOW_DISSECTOR);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_prog_detach");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Expect no prog attached after successful detach */
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_create_close_query(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts);
|
||||
int link;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
close(link);
|
||||
/* Expect no prog attached after closing last link FD */
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_update_no_old_prog(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);
|
||||
int err, link;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect success replacing the prog when old prog not specified */
|
||||
update_opts.flags = 0;
|
||||
update_opts.old_prog_fd = 0;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("bpf_link_update");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));
|
||||
|
||||
close(link);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_update_replace_old_prog(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);
|
||||
int err, link;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect success F_REPLACE and old prog specified to succeed */
|
||||
update_opts.flags = BPF_F_REPLACE;
|
||||
update_opts.old_prog_fd = prog1;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("bpf_link_update");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog2));
|
||||
|
||||
close(link);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_update_invalid_opts(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);
|
||||
int err, link;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect update to fail w/ old prog FD but w/o F_REPLACE*/
|
||||
errno = 0;
|
||||
update_opts.flags = 0;
|
||||
update_opts.old_prog_fd = prog1;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != EINVAL)) {
|
||||
perror("bpf_link_update expected EINVAL");
|
||||
goto out_close;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect update to fail on old prog FD mismatch */
|
||||
errno = 0;
|
||||
update_opts.flags = BPF_F_REPLACE;
|
||||
update_opts.old_prog_fd = prog2;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != EPERM)) {
|
||||
perror("bpf_link_update expected EPERM");
|
||||
goto out_close;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect update to fail for invalid old prog FD */
|
||||
errno = 0;
|
||||
update_opts.flags = BPF_F_REPLACE;
|
||||
update_opts.old_prog_fd = -1;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != EBADF)) {
|
||||
perror("bpf_link_update expected EBADF");
|
||||
goto out_close;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect update to fail with invalid flags */
|
||||
errno = 0;
|
||||
update_opts.flags = BPF_F_ALLOW_MULTI;
|
||||
update_opts.old_prog_fd = 0;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != EINVAL))
|
||||
perror("bpf_link_update expected EINVAL");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
out_close:
|
||||
close(prog_fd[1]);
|
||||
close(prog_fd[0]);
|
||||
close(link);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_update_invalid_prog(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);
|
||||
int err, link, prog3;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
/* Expect failure when new prog FD is not valid */
|
||||
errno = 0;
|
||||
update_opts.flags = 0;
|
||||
update_opts.old_prog_fd = 0;
|
||||
err = bpf_link_update(link, -1, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != EBADF)) {
|
||||
perror("bpf_link_update expected EINVAL");
|
||||
goto out_close_link;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
prog3 = load_prog(BPF_PROG_TYPE_SOCKET_FILTER);
|
||||
if (prog3 < 0)
|
||||
goto out_close_link;
|
||||
|
||||
/* Expect failure when new prog FD type doesn't match */
|
||||
errno = 0;
|
||||
update_opts.flags = 0;
|
||||
update_opts.old_prog_fd = 0;
|
||||
err = bpf_link_update(link, prog3, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != EINVAL))
|
||||
perror("bpf_link_update expected EINVAL");
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
close(prog3);
|
||||
out_close_link:
|
||||
close(link);
|
||||
CHECK_FAIL(prog_is_attached(netns));
|
||||
}
|
||||
|
||||
static void test_link_update_netns_gone(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);
|
||||
int err, link, old_net;
|
||||
|
||||
old_net = netns;
|
||||
netns = unshare_net(old_net);
|
||||
if (netns < 0)
|
||||
return;
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
return;
|
||||
}
|
||||
CHECK_FAIL(query_attached_prog_id(netns) != query_prog_id(prog1));
|
||||
|
||||
close(netns);
|
||||
err = setns(old_net, CLONE_NEWNET);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("setns(CLONE_NEWNET)");
|
||||
close(link);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Expect failure when netns destroyed */
|
||||
errno = 0;
|
||||
update_opts.flags = 0;
|
||||
update_opts.old_prog_fd = 0;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(!err || errno != ENOLINK))
|
||||
perror("bpf_link_update");
|
||||
|
||||
close(link);
|
||||
}
|
||||
|
||||
static void test_link_get_info(int netns, int prog1, int prog2)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_create_opts, create_opts);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts);
|
||||
struct bpf_link_info info = {};
|
||||
struct stat netns_stat = {};
|
||||
__u32 info_len, link_id;
|
||||
int err, link, old_net;
|
||||
|
||||
old_net = netns;
|
||||
netns = unshare_net(old_net);
|
||||
if (netns < 0)
|
||||
return;
|
||||
|
||||
err = fstat(netns, &netns_stat);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("stat(netns)");
|
||||
goto out_resetns;
|
||||
}
|
||||
|
||||
link = bpf_link_create(prog1, netns, BPF_FLOW_DISSECTOR, &create_opts);
|
||||
if (CHECK_FAIL(link < 0)) {
|
||||
perror("bpf_link_create(prog1)");
|
||||
goto out_resetns;
|
||||
}
|
||||
|
||||
info_len = sizeof(info);
|
||||
err = bpf_obj_get_info_by_fd(link, &info, &info_len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_obj_get_info");
|
||||
goto out_unlink;
|
||||
}
|
||||
CHECK_FAIL(info_len != sizeof(info));
|
||||
|
||||
/* Expect link info to be sane and match prog and netns details */
|
||||
CHECK_FAIL(info.type != BPF_LINK_TYPE_NETNS);
|
||||
CHECK_FAIL(info.id == 0);
|
||||
CHECK_FAIL(info.prog_id != query_prog_id(prog1));
|
||||
CHECK_FAIL(info.netns.netns_ino != netns_stat.st_ino);
|
||||
CHECK_FAIL(info.netns.attach_type != BPF_FLOW_DISSECTOR);
|
||||
|
||||
update_opts.flags = 0;
|
||||
update_opts.old_prog_fd = 0;
|
||||
err = bpf_link_update(link, prog2, &update_opts);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_link_update(prog2)");
|
||||
goto out_unlink;
|
||||
}
|
||||
|
||||
link_id = info.id;
|
||||
info_len = sizeof(info);
|
||||
err = bpf_obj_get_info_by_fd(link, &info, &info_len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_obj_get_info");
|
||||
goto out_unlink;
|
||||
}
|
||||
CHECK_FAIL(info_len != sizeof(info));
|
||||
|
||||
/* Expect no info change after update except in prog id */
|
||||
CHECK_FAIL(info.type != BPF_LINK_TYPE_NETNS);
|
||||
CHECK_FAIL(info.id != link_id);
|
||||
CHECK_FAIL(info.prog_id != query_prog_id(prog2));
|
||||
CHECK_FAIL(info.netns.netns_ino != netns_stat.st_ino);
|
||||
CHECK_FAIL(info.netns.attach_type != BPF_FLOW_DISSECTOR);
|
||||
|
||||
/* Leave netns link is attached to and close last FD to it */
|
||||
err = setns(old_net, CLONE_NEWNET);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("setns(NEWNET)");
|
||||
goto out_unlink;
|
||||
}
|
||||
close(netns);
|
||||
old_net = -1;
|
||||
netns = -1;
|
||||
|
||||
info_len = sizeof(info);
|
||||
err = bpf_obj_get_info_by_fd(link, &info, &info_len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_obj_get_info");
|
||||
goto out_unlink;
|
||||
}
|
||||
CHECK_FAIL(info_len != sizeof(info));
|
||||
|
||||
/* Expect netns_ino to change to 0 */
|
||||
CHECK_FAIL(info.type != BPF_LINK_TYPE_NETNS);
|
||||
CHECK_FAIL(info.id != link_id);
|
||||
CHECK_FAIL(info.prog_id != query_prog_id(prog2));
|
||||
CHECK_FAIL(info.netns.netns_ino != 0);
|
||||
CHECK_FAIL(info.netns.attach_type != BPF_FLOW_DISSECTOR);
|
||||
|
||||
out_unlink:
|
||||
close(link);
|
||||
out_resetns:
|
||||
if (old_net != -1)
|
||||
setns(old_net, CLONE_NEWNET);
|
||||
if (netns != -1)
|
||||
close(netns);
|
||||
}
|
||||
|
||||
static void run_tests(int netns)
|
||||
{
|
||||
struct test {
|
||||
const char *test_name;
|
||||
void (*test_func)(int netns, int prog1, int prog2);
|
||||
} tests[] = {
|
||||
{ "prog attach, prog attach",
|
||||
test_prog_attach_prog_attach },
|
||||
{ "link create, link create",
|
||||
test_link_create_link_create },
|
||||
{ "prog attach, link create",
|
||||
test_prog_attach_link_create },
|
||||
{ "link create, prog attach",
|
||||
test_link_create_prog_attach },
|
||||
{ "link create, prog detach",
|
||||
test_link_create_prog_detach },
|
||||
{ "prog attach, detach, query",
|
||||
test_prog_attach_detach_query },
|
||||
{ "link create, close, query",
|
||||
test_link_create_close_query },
|
||||
{ "link update no old prog",
|
||||
test_link_update_no_old_prog },
|
||||
{ "link update with replace old prog",
|
||||
test_link_update_replace_old_prog },
|
||||
{ "link update invalid opts",
|
||||
test_link_update_invalid_opts },
|
||||
{ "link update invalid prog",
|
||||
test_link_update_invalid_prog },
|
||||
{ "link update netns gone",
|
||||
test_link_update_netns_gone },
|
||||
{ "link get info",
|
||||
test_link_get_info },
|
||||
};
|
||||
int i, progs[2] = { -1, -1 };
|
||||
char test_name[80];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(progs); i++) {
|
||||
progs[i] = load_prog(BPF_PROG_TYPE_FLOW_DISSECTOR);
|
||||
if (progs[i] < 0)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
snprintf(test_name, sizeof(test_name),
|
||||
"flow dissector %s%s",
|
||||
tests[i].test_name,
|
||||
netns == init_net ? " (init_net)" : "");
|
||||
if (test__start_subtest(test_name))
|
||||
tests[i].test_func(netns, progs[0], progs[1]);
|
||||
}
|
||||
out_close:
|
||||
for (i = 0; i < ARRAY_SIZE(progs); i++) {
|
||||
if (progs[i] != -1)
|
||||
CHECK_FAIL(close(progs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void test_flow_dissector_reattach(void)
|
||||
{
|
||||
int init_net, self_net, err;
|
||||
int err, new_net, saved_net;
|
||||
|
||||
self_net = open("/proc/self/ns/net", O_RDONLY);
|
||||
if (CHECK_FAIL(self_net < 0)) {
|
||||
saved_net = open("/proc/self/ns/net", O_RDONLY);
|
||||
if (CHECK_FAIL(saved_net < 0)) {
|
||||
perror("open(/proc/self/ns/net");
|
||||
return;
|
||||
}
|
||||
@ -111,30 +626,29 @@ void test_flow_dissector_reattach(void)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (is_attached(init_net)) {
|
||||
if (prog_is_attached(init_net)) {
|
||||
test__skip();
|
||||
printf("Can't test with flow dissector attached to init_net\n");
|
||||
goto out_setns;
|
||||
}
|
||||
|
||||
/* First run tests in root network namespace */
|
||||
do_flow_dissector_reattach();
|
||||
run_tests(init_net);
|
||||
|
||||
/* Then repeat tests in a non-root namespace */
|
||||
err = unshare(CLONE_NEWNET);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("unshare(CLONE_NEWNET)");
|
||||
new_net = unshare_net(init_net);
|
||||
if (new_net < 0)
|
||||
goto out_setns;
|
||||
}
|
||||
do_flow_dissector_reattach();
|
||||
run_tests(new_net);
|
||||
close(new_net);
|
||||
|
||||
out_setns:
|
||||
/* Move back to netns we started in. */
|
||||
err = setns(self_net, CLONE_NEWNET);
|
||||
err = setns(saved_net, CLONE_NEWNET);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("setns(/proc/self/ns/net)");
|
||||
|
||||
out_close:
|
||||
close(init_net);
|
||||
close(self_net);
|
||||
close(saved_net);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user