1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/bpf_insn.h>
7 #include "bpf-firewall.h"
8 #include "bpf-program.h"
9 #include "in-addr-prefix-util.h"
10 #include "load-fragment.h"
12 #include "memory-util.h"
16 #include "unit-serialize.h"
19 int main(int argc
, char *argv
[]) {
20 const struct bpf_insn exit_insn
[] = {
21 BPF_MOV64_IMM(BPF_REG_0
, 0), /* drop */
25 _cleanup_(rm_rf_physical_and_freep
) char *runtime_dir
= NULL
;
26 CGroupContext
*cc
= NULL
;
27 _cleanup_(bpf_program_unrefp
) BPFProgram
*p
= NULL
;
28 _cleanup_(manager_freep
) Manager
*m
= NULL
;
34 bool test_custom_filter
= false;
35 const char *test_prog
= "/sys/fs/bpf/test-dropper";
37 test_setup_logging(LOG_DEBUG
);
39 if (detect_container() > 0)
40 return log_tests_skipped("test-bpf-firewall fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666");
42 assert_se(getrlimit(RLIMIT_MEMLOCK
, &rl
) >= 0);
43 rl
.rlim_cur
= rl
.rlim_max
= MAX(rl
.rlim_max
, CAN_MEMLOCK_SIZE
);
44 (void) setrlimit(RLIMIT_MEMLOCK
, &rl
);
47 return log_tests_skipped("Can't use mlock()");
49 r
= enter_cgroup_subroot(NULL
);
51 return log_tests_skipped("cgroupfs not available");
53 _cleanup_free_
char *unit_dir
= NULL
;
54 assert_se(get_testdata_dir("units", &unit_dir
) >= 0);
55 assert_se(set_unit_path(unit_dir
) >= 0);
56 assert_se(runtime_dir
= setup_fake_runtime_dir());
58 r
= bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB
, &p
);
61 r
= bpf_program_add_instructions(p
, exit_insn
, ELEMENTSOF(exit_insn
));
65 return log_tests_skipped("not running as root");
67 r
= bpf_firewall_supported();
68 if (r
== BPF_FIREWALL_UNSUPPORTED
)
69 return log_tests_skipped("BPF firewalling not supported");
72 if (r
== BPF_FIREWALL_SUPPORTED_WITH_MULTI
) {
73 log_notice("BPF firewalling with BPF_F_ALLOW_MULTI supported. Yay!");
74 test_custom_filter
= true;
76 log_notice("BPF firewalling (though without BPF_F_ALLOW_MULTI) supported. Good.");
78 r
= bpf_program_load_kernel(p
, log_buf
, ELEMENTSOF(log_buf
));
81 if (test_custom_filter
) {
83 attr
.pathname
= PTR_TO_UINT64(test_prog
);
84 attr
.bpf_fd
= p
->kernel_fd
;
87 (void) unlink(test_prog
);
89 r
= bpf(BPF_OBJ_PIN
, &attr
, sizeof(attr
));
91 log_warning_errno(errno
, "BPF object pinning failed, will not run custom filter test: %m");
92 test_custom_filter
= false;
96 p
= bpf_program_unref(p
);
98 /* The simple tests succeeded. Now let's try full unit-based use-case. */
100 assert_se(manager_new(UNIT_FILE_USER
, MANAGER_TEST_RUN_BASIC
, &m
) >= 0);
101 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
103 assert_se(u
= unit_new(m
, sizeof(Service
)));
104 assert_se(unit_add_name(u
, "foo.service") == 0);
105 assert_se(cc
= unit_get_cgroup_context(u
));
108 cc
->ip_accounting
= true;
110 assert_se(config_parse_in_addr_prefixes(u
->id
, "filename", 1, "Service", 1, "IPAddressAllow", 0, "10.0.1.0/24", &cc
->ip_address_allow
, NULL
) == 0);
111 assert_se(config_parse_in_addr_prefixes(u
->id
, "filename", 1, "Service", 1, "IPAddressAllow", 0, "127.0.0.2", &cc
->ip_address_allow
, NULL
) == 0);
112 assert_se(config_parse_in_addr_prefixes(u
->id
, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.3", &cc
->ip_address_deny
, NULL
) == 0);
113 assert_se(config_parse_in_addr_prefixes(u
->id
, "filename", 1, "Service", 1, "IPAddressDeny", 0, "10.0.3.2/24", &cc
->ip_address_deny
, NULL
) == 0);
114 assert_se(config_parse_in_addr_prefixes(u
->id
, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.1/25", &cc
->ip_address_deny
, NULL
) == 0);
115 assert_se(config_parse_in_addr_prefixes(u
->id
, "filename", 1, "Service", 1, "IPAddressDeny", 0, "127.0.0.4", &cc
->ip_address_deny
, NULL
) == 0);
117 assert_se(set_size(cc
->ip_address_allow
) == 2);
118 assert_se(set_size(cc
->ip_address_deny
) == 4);
120 /* The deny list is defined redundantly, let's ensure it will be properly reduced */
121 assert_se(in_addr_prefixes_reduce(cc
->ip_address_allow
) >= 0);
122 assert_se(in_addr_prefixes_reduce(cc
->ip_address_deny
) >= 0);
124 assert_se(set_size(cc
->ip_address_allow
) == 2);
125 assert_se(set_size(cc
->ip_address_deny
) == 2);
127 assert_se(set_contains(cc
->ip_address_allow
, &(struct in_addr_prefix
) {
129 .address
.in
.s_addr
= htobe32((UINT32_C(10) << 24) | (UINT32_C(1) << 8)),
131 assert_se(set_contains(cc
->ip_address_allow
, &(struct in_addr_prefix
) {
133 .address
.in
.s_addr
= htobe32(0x7f000002),
135 assert_se(set_contains(cc
->ip_address_deny
, &(struct in_addr_prefix
) {
137 .address
.in
.s_addr
= htobe32(0x7f000000),
139 assert_se(set_contains(cc
->ip_address_deny
, &(struct in_addr_prefix
) {
141 .address
.in
.s_addr
= htobe32((UINT32_C(10) << 24) | (UINT32_C(3) << 8)),
144 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);
145 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);
147 assert_se(SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]);
148 assert_se(SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->command_next
);
149 assert_se(!SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->command_next
->command_next
);
151 SERVICE(u
)->type
= SERVICE_ONESHOT
;
152 u
->load_state
= UNIT_LOADED
;
154 unit_dump(u
, stdout
, NULL
);
156 r
= bpf_firewall_compile(u
);
157 if (IN_SET(r
, -ENOTTY
, -ENOSYS
, -EPERM
))
158 return log_tests_skipped("Kernel doesn't support the necessary bpf bits (masked out via seccomp?)");
161 assert_se(u
->ip_bpf_ingress
);
162 assert_se(u
->ip_bpf_egress
);
164 r
= bpf_program_load_kernel(u
->ip_bpf_ingress
, log_buf
, ELEMENTSOF(log_buf
));
167 log_notice("-------");
168 log_notice("%s", log_buf
);
169 log_notice("-------");
173 r
= bpf_program_load_kernel(u
->ip_bpf_egress
, log_buf
, ELEMENTSOF(log_buf
));
176 log_notice("-------");
177 log_notice("%s", log_buf
);
178 log_notice("-------");
182 assert_se(unit_start(u
) >= 0);
184 while (!IN_SET(SERVICE(u
)->state
, SERVICE_DEAD
, SERVICE_FAILED
))
185 assert_se(sd_event_run(m
->event
, UINT64_MAX
) >= 0);
187 assert_se(SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->exec_status
.code
== CLD_EXITED
&&
188 SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->exec_status
.status
== EXIT_SUCCESS
);
190 assert_se(SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->command_next
->exec_status
.code
!= CLD_EXITED
||
191 SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->command_next
->exec_status
.status
!= EXIT_SUCCESS
);
193 if (test_custom_filter
) {
194 assert_se(u
= unit_new(m
, sizeof(Service
)));
195 assert_se(unit_add_name(u
, "custom-filter.service") == 0);
196 assert_se(cc
= unit_get_cgroup_context(u
));
199 cc
->ip_accounting
= true;
201 assert_se(config_parse_ip_filter_bpf_progs(u
->id
, "filename", 1, "Service", 1, "IPIngressFilterPath", 0, test_prog
, &cc
->ip_filters_ingress
, u
) == 0);
202 assert_se(config_parse_exec(u
->id
, "filename", 1, "Service", 1, "ExecStart", SERVICE_EXEC_START
, "-/bin/ping -c 1 127.0.0.1 -W 5", SERVICE(u
)->exec_command
, u
) == 0);
204 SERVICE(u
)->type
= SERVICE_ONESHOT
;
205 u
->load_state
= UNIT_LOADED
;
207 assert_se(unit_start(u
) >= 0);
209 while (!IN_SET(SERVICE(u
)->state
, SERVICE_DEAD
, SERVICE_FAILED
))
210 assert_se(sd_event_run(m
->event
, UINT64_MAX
) >= 0);
212 assert_se(SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->exec_status
.code
!= CLD_EXITED
||
213 SERVICE(u
)->exec_command
[SERVICE_EXEC_START
]->exec_status
.status
!= EXIT_SUCCESS
);
215 (void) unlink(test_prog
);
216 assert_se(SERVICE(u
)->state
== SERVICE_DEAD
);