// SPDX-License-Identifier: GPL-2.0 /* * Test that the flow_dissector program can be updated with a single * syscall by attaching a new program that replaces the existing one. * * Corner case - the same program cannot be attached twice. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "test_progs.h" static bool is_attached(int netns) { __u32 cnt; int err; err = bpf_prog_query(netns, BPF_FLOW_DISSECTOR, 0, NULL, NULL, &cnt); if (CHECK_FAIL(err)) { perror("bpf_prog_query"); return true; /* fail-safe */ } return cnt > 0; } static int load_prog(void) { struct bpf_insn prog[] = { BPF_MOV64_IMM(BPF_REG_0, BPF_OK), BPF_EXIT_INSN(), }; int fd; fd = bpf_load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, 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) { int prog_fd[2] = { -1, -1 }; 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; } /* Expect success when attaching a different program */ err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0); if (CHECK_FAIL(err)) { perror("bpf_prog_attach-1"); goto out_detach; } /* Expect failure when attaching the same program twice */ err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0); if (CHECK_FAIL(!err || errno != EINVAL)) perror("bpf_prog_attach-2"); out_detach: err = bpf_prog_detach(0, BPF_FLOW_DISSECTOR); if (CHECK_FAIL(err)) perror("bpf_prog_detach"); out_close: close(prog_fd[1]); close(prog_fd[0]); } void test_flow_dissector_reattach(void) { int init_net, self_net, err; self_net = open("/proc/self/ns/net", O_RDONLY); if (CHECK_FAIL(self_net < 0)) { perror("open(/proc/self/ns/net"); return; } init_net = open("/proc/1/ns/net", O_RDONLY); if (CHECK_FAIL(init_net < 0)) { perror("open(/proc/1/ns/net)"); goto out_close; } err = setns(init_net, CLONE_NEWNET); if (CHECK_FAIL(err)) { perror("setns(/proc/1/ns/net)"); goto out_close; } if (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(); /* Then repeat tests in a non-root namespace */ err = unshare(CLONE_NEWNET); if (CHECK_FAIL(err)) { perror("unshare(CLONE_NEWNET)"); goto out_setns; } do_flow_dissector_reattach(); out_setns: /* Move back to netns we started in. */ err = setns(self_net, CLONE_NEWNET); if (CHECK_FAIL(err)) perror("setns(/proc/self/ns/net)"); out_close: close(init_net); close(self_net); }