Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | // 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 <errno.h> #include <fcntl.h> #include <sched.h> #include <stdbool.h> #include <unistd.h> #include <linux/bpf.h> #include <bpf/bpf.h> #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); } |