]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bpf: load firewall with name only if supported
authorJulia Kartseva <hex@fb.com>
Fri, 28 Jan 2022 00:36:25 +0000 (16:36 -0800)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 28 Jan 2022 03:42:18 +0000 (12:42 +0900)
BPF firewall is supported starting from v4.9 kernel where
BPF_PROG_TYPE_SOCKET_FILTER support was added [0].

However, program name support was added to v4.15 [1] and BPF_PROG_LOAD
syscall will fail on older kernels if called with prog_name attribute.
BPF_F_ALLOW_MULTI was also added to v4.15 kernel which allows reusing
BPF_F_ALLOW_MULTI probe to indicate that program name is also supported.

It is no problem for BPF_PROG_TYPE_CGROUP_DEVICE since it was added in
v4.15.

[0] https://elixir.bootlin.com/linux/v4.9/source/include/uapi/linux/bpf.h#L92
[1] https://elixir.bootlin.com/linux/v4.15/source/include/uapi/linux/bpf.h#L191

Follow-up of https://github.com/systemd/systemd/pull/22214

src/core/bpf-devices.c
src/core/bpf-firewall.c

index e3100b862b7bd8a035d1be055e70f429fd04fc23..f62c6f193120a0cd23a575eff26872f40375f035 100644 (file)
@@ -306,7 +306,7 @@ int bpf_devices_supported(void) {
                 return supported = 0;
         }
 
-        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, NULL, &program);
+        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &program);
         if (r < 0) {
                 log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
                 return supported = 0;
index 8158fafc8ec038ff6e5ab2327c7928e8c7b32bb3..0297053add595efec4559afc1815407dbdf8812a 100644 (file)
@@ -145,6 +145,7 @@ static int add_instructions_for_ip_any(
 
 static int bpf_firewall_compile_bpf(
                 Unit *u,
+                const char *prog_name,
                 bool is_ingress,
                 BPFProgram **ret,
                 bool ip_allow_any,
@@ -193,7 +194,6 @@ static int bpf_firewall_compile_bpf(
         };
 
         _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
-        const char *prog_name = is_ingress ? "sd_fw_ingress" : "sd_fw_egress";
         int accounting_map_fd, r;
         bool access_enabled;
 
@@ -527,9 +527,10 @@ static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, int *fd_i
 }
 
 int bpf_firewall_compile(Unit *u) {
+        const char *ingress_name = NULL, *egress_name = NULL;
+        bool ip_allow_any = false, ip_deny_any = false;
         CGroupContext *cc;
         int r, supported;
-        bool ip_allow_any = false, ip_deny_any = false;
 
         assert(u);
 
@@ -552,6 +553,13 @@ int bpf_firewall_compile(Unit *u) {
                 return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
                                             "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
 
+        /* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15
+         * kernel). */
+        if (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
+                ingress_name = "sd_fw_ingress";
+                egress_name = "sd_fw_egress";
+        }
+
         /* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves,
          * but we reuse the accounting maps. That way the firewall in effect always maps to the actual
          * configuration, but we don't flush out the accounting unnecessarily */
@@ -585,11 +593,11 @@ int bpf_firewall_compile(Unit *u) {
         if (r < 0)
                 return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m");
 
-        r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
+        r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
         if (r < 0)
                 return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m");
 
-        r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
+        r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
         if (r < 0)
                 return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m");
 
@@ -826,6 +834,7 @@ int bpf_firewall_supported(void) {
                 return supported = BPF_FIREWALL_UNSUPPORTED;
         }
 
+        /* prog_name is NULL since it is supported only starting from v4.15 kernel. */
         r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program);
         if (r < 0) {
                 bpf_firewall_unsupported_reason =
@@ -883,7 +892,9 @@ int bpf_firewall_supported(void) {
         /* So now we know that the BPF program is generally available, let's see if BPF_F_ALLOW_MULTI is also supported
          * (which was added in kernel 4.15). We use a similar logic as before, but this time we use the BPF_PROG_ATTACH
          * bpf() call and the BPF_F_ALLOW_MULTI flags value. Since the flags are checked early in the system call we'll
-         * get EINVAL if it's not supported, and EBADF as before if it is available. */
+         * get EINVAL if it's not supported, and EBADF as before if it is available.
+         * Use probe result as the indicator that program name is also supported since they both were
+         * added in kernel 4.15. */
 
         zero(attr);
         attr.attach_type = BPF_CGROUP_INET_EGRESS;