]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
db3a5930 DM |
2 | |
3 | #include <linux/libbpf.h> | |
4 | #include <string.h> | |
3dffcfc7 | 5 | #include <sys/mman.h> |
db3a5930 DM |
6 | #include <unistd.h> |
7 | ||
8 | #include "bpf-firewall.h" | |
9 | #include "bpf-program.h" | |
10 | #include "load-fragment.h" | |
11 | #include "manager.h" | |
12 | #include "rm-rf.h" | |
13 | #include "service.h" | |
14 | #include "test-helper.h" | |
15 | #include "tests.h" | |
16 | #include "unit.h" | |
17 | ||
3dffcfc7 LP |
18 | /* We use the same limit here that PID 1 bumps RLIMIT_MEMLOCK to if it can */ |
19 | #define CAN_MEMLOCK_SIZE (64U*1024U*1024U) | |
20 | ||
21 | static bool can_memlock(void) { | |
22 | void *p; | |
23 | bool b; | |
24 | ||
25 | /* Let's see if we can mlock() a larger blob of memory. BPF programs are charged against | |
26 | * RLIMIT_MEMLOCK, hence let's first make sure we can lock memory at all, and skip the test if we | |
27 | * cannot. Why not check RLIMIT_MEMLOCK explicitly? Because in container environments the | |
28 | * RLIMIT_MEMLOCK value we see might not match the RLIMIT_MEMLOCK value actually in effect. */ | |
29 | ||
30 | p = mmap(NULL, CAN_MEMLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); | |
31 | if (p == MAP_FAILED) | |
32 | return false; | |
33 | ||
34 | b = mlock(p, CAN_MEMLOCK_SIZE) >= 0; | |
35 | if (b) | |
36 | assert_se(munlock(p, CAN_MEMLOCK_SIZE) >= 0); | |
37 | ||
38 | assert_se(munmap(p, CAN_MEMLOCK_SIZE) >= 0); | |
39 | return b; | |
40 | } | |
41 | ||
db3a5930 DM |
42 | int main(int argc, char *argv[]) { |
43 | struct bpf_insn exit_insn[] = { | |
44 | BPF_MOV64_IMM(BPF_REG_0, 1), | |
45 | BPF_EXIT_INSN() | |
46 | }; | |
47 | ||
48 | _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; | |
49 | CGroupContext *cc = NULL; | |
50 | _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; | |
c70cac54 | 51 | _cleanup_(manager_freep) Manager *m = NULL; |
db3a5930 DM |
52 | Unit *u; |
53 | char log_buf[65535]; | |
3dffcfc7 | 54 | struct rlimit rl; |
db3a5930 DM |
55 | int r; |
56 | ||
6d7c4033 | 57 | test_setup_logging(LOG_DEBUG); |
db3a5930 | 58 | |
f9cf3491 EV |
59 | if (is_run_on_travis_ci()) |
60 | return log_tests_skipped("test-bpf fails on Travis CI: https://github.com/systemd/systemd/issues/9666"); | |
61 | ||
3dffcfc7 LP |
62 | assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0); |
63 | rl.rlim_cur = rl.rlim_max = MAX3(rl.rlim_cur, rl.rlim_max, CAN_MEMLOCK_SIZE); | |
64 | (void) setrlimit(RLIMIT_MEMLOCK, &rl); | |
65 | ||
66 | if (!can_memlock()) | |
67 | return log_tests_skipped("Can't use mlock(), skipping."); | |
68 | ||
651d47d1 | 69 | r = enter_cgroup_subroot(); |
317bb217 ZJS |
70 | if (r == -ENOMEDIUM) |
71 | return log_tests_skipped("cgroupfs not available"); | |
651d47d1 | 72 | |
55890a40 | 73 | assert_se(set_unit_path(get_testdata_dir()) >= 0); |
db3a5930 DM |
74 | assert_se(runtime_dir = setup_fake_runtime_dir()); |
75 | ||
76 | r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p); | |
77 | assert(r == 0); | |
78 | ||
79 | r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn)); | |
80 | assert(r == 0); | |
81 | ||
317bb217 ZJS |
82 | if (getuid() != 0) |
83 | return log_tests_skipped("not running as root"); | |
db3a5930 DM |
84 | |
85 | r = bpf_firewall_supported(); | |
317bb217 ZJS |
86 | if (r == BPF_FIREWALL_UNSUPPORTED) |
87 | return log_tests_skipped("BPF firewalling not supported"); | |
db3a5930 DM |
88 | assert_se(r > 0); |
89 | ||
2ae7ee58 LP |
90 | if (r == BPF_FIREWALL_SUPPORTED_WITH_MULTI) |
91 | log_notice("BPF firewalling with BPF_F_ALLOW_MULTI supported. Yay!"); | |
92 | else | |
93 | log_notice("BPF firewalling (though without BPF_F_ALLOW_MULTI) supported. Good."); | |
94 | ||
db3a5930 DM |
95 | r = bpf_program_load_kernel(p, log_buf, ELEMENTSOF(log_buf)); |
96 | assert(r >= 0); | |
97 | ||
98 | p = bpf_program_unref(p); | |
99 | ||
100 | /* The simple tests suceeded. Now let's try full unit-based use-case. */ | |
101 | ||
ed4ac965 | 102 | assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); |
db3a5930 DM |
103 | assert_se(manager_startup(m, NULL, NULL) >= 0); |
104 | ||
105 | assert_se(u = unit_new(m, sizeof(Service))); | |
106 | assert_se(unit_add_name(u, "foo.service") == 0); | |
107 | assert_se(cc = unit_get_cgroup_context(u)); | |
108 | u->perpetual = true; | |
109 | ||
110 | cc->ip_accounting = true; | |
111 | ||
112 | assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc->ip_address_allow, NULL) == 0); | |
113 | assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc->ip_address_allow, NULL) == 0); | |
114 | assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc->ip_address_deny, NULL) == 0); | |
115 | assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc->ip_address_deny, NULL) == 0); | |
116 | assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc->ip_address_deny, NULL) == 0); | |
117 | assert_se(config_parse_ip_address_access(u->id, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc->ip_address_deny, NULL) == 0); | |
118 | ||
119 | assert(cc->ip_address_allow); | |
120 | assert(cc->ip_address_allow->items_next); | |
121 | assert(!cc->ip_address_allow->items_next->items_next); | |
122 | ||
123 | /* The deny list is defined redundantly, let's ensure it got properly reduced */ | |
124 | assert(cc->ip_address_deny); | |
125 | assert(cc->ip_address_deny->items_next); | |
126 | assert(!cc->ip_address_deny->items_next->items_next); | |
127 | ||
02ec6e04 DJL |
128 | assert_se(config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart", SERVICE_EXEC_START, "/bin/ping -c 1 127.0.0.2 -W 5", SERVICE(u)->exec_command, u) == 0); |
129 | assert_se(config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart", SERVICE_EXEC_START, "/bin/ping -c 1 127.0.0.3 -W 5", SERVICE(u)->exec_command, u) == 0); | |
db3a5930 DM |
130 | |
131 | assert_se(SERVICE(u)->exec_command[SERVICE_EXEC_START]); | |
132 | assert_se(SERVICE(u)->exec_command[SERVICE_EXEC_START]->command_next); | |
133 | assert_se(!SERVICE(u)->exec_command[SERVICE_EXEC_START]->command_next->command_next); | |
134 | ||
135 | SERVICE(u)->type = SERVICE_ONESHOT; | |
136 | u->load_state = UNIT_LOADED; | |
137 | ||
138 | unit_dump(u, stdout, NULL); | |
139 | ||
140 | r = bpf_firewall_compile(u); | |
317bb217 ZJS |
141 | if (IN_SET(r, -ENOTTY, -ENOSYS, -EPERM)) |
142 | return log_tests_skipped("Kernel doesn't support the necessary bpf bits (masked out via seccomp?)"); | |
db3a5930 DM |
143 | assert_se(r >= 0); |
144 | ||
145 | assert(u->ip_bpf_ingress); | |
146 | assert(u->ip_bpf_egress); | |
147 | ||
148 | r = bpf_program_load_kernel(u->ip_bpf_ingress, log_buf, ELEMENTSOF(log_buf)); | |
149 | ||
150 | log_notice("log:"); | |
151 | log_notice("-------"); | |
152 | log_notice("%s", log_buf); | |
153 | log_notice("-------"); | |
154 | ||
155 | assert(r >= 0); | |
156 | ||
157 | r = bpf_program_load_kernel(u->ip_bpf_egress, log_buf, ELEMENTSOF(log_buf)); | |
158 | ||
159 | log_notice("log:"); | |
160 | log_notice("-------"); | |
161 | log_notice("%s", log_buf); | |
162 | log_notice("-------"); | |
163 | ||
164 | assert(r >= 0); | |
165 | ||
85e55d14 | 166 | assert_se(unit_start(u) >= 0); |
db3a5930 DM |
167 | |
168 | while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) | |
169 | assert_se(sd_event_run(m->event, UINT64_MAX) >= 0); | |
170 | ||
171 | assert_se(SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code == CLD_EXITED && | |
172 | SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.status == EXIT_SUCCESS); | |
173 | ||
174 | assert_se(SERVICE(u)->exec_command[SERVICE_EXEC_START]->command_next->exec_status.code != CLD_EXITED || | |
175 | SERVICE(u)->exec_command[SERVICE_EXEC_START]->command_next->exec_status.status != EXIT_SUCCESS); | |
176 | ||
db3a5930 DM |
177 | return 0; |
178 | } |