| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include "fd-util.h" |
| #include "restrict-ifaces.h" |
| #include "netlink-util.h" |
| |
| #if BPF_FRAMEWORK |
| /* libbpf, clang and llc compile time dependencies are satisfied */ |
| |
| #include "bpf-dlopen.h" |
| #include "bpf-link.h" |
| #include "bpf-util.h" |
| #include "bpf/restrict_ifaces/restrict-ifaces-skel.h" |
| |
| static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) { |
| restrict_ifaces_bpf__destroy(obj); |
| return NULL; |
| } |
| |
| DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_ifaces_bpf *, restrict_ifaces_bpf_free); |
| |
| static int prepare_restrict_ifaces_bpf( |
| Unit* u, |
| bool is_allow_list, |
| const Set *restrict_network_interfaces, |
| struct restrict_ifaces_bpf **ret_object) { |
| |
| _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL; |
| _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
| char *iface; |
| int r, map_fd; |
| |
| assert(ret_object); |
| |
| obj = restrict_ifaces_bpf__open(); |
| if (!obj) |
| return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "restrict-interfaces: Failed to open BPF object: %m"); |
| |
| r = sym_bpf_map__set_max_entries(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u)); |
| if (r != 0) |
| return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, |
| "restrict-interfaces: Failed to resize BPF map '%s': %m", |
| sym_bpf_map__name(obj->maps.sd_restrictif)); |
| |
| obj->rodata->is_allow_list = is_allow_list; |
| |
| r = restrict_ifaces_bpf__load(obj); |
| if (r != 0) |
| return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, r, "restrict-interfaces: Failed to load BPF object: %m"); |
| |
| map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif); |
| |
| SET_FOREACH(iface, restrict_network_interfaces) { |
| uint8_t dummy = 0; |
| int ifindex; |
| |
| ifindex = rtnl_resolve_interface(&rtnl, iface); |
| if (ifindex < 0) { |
| log_unit_warning_errno(u, ifindex, |
| "restrict-interfaces: Couldn't find index of network interface '%s', ignoring: %m", |
| iface); |
| continue; |
| } |
| |
| if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY)) |
| return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, |
| "restrict-interfaces: Failed to update BPF map '%s' fd: %m", |
| sym_bpf_map__name(obj->maps.sd_restrictif)); |
| } |
| |
| *ret_object = TAKE_PTR(obj); |
| return 0; |
| } |
| |
| int restrict_network_interfaces_supported(void) { |
| _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL; |
| static int supported = -1; |
| int r; |
| |
| if (supported >= 0) |
| return supported; |
| |
| if (!cgroup_bpf_supported()) |
| return (supported = false); |
| |
| if (!compat_libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*opts=*/NULL)) { |
| log_debug("restrict-interfaces: BPF program type cgroup_skb is not supported"); |
| return (supported = false); |
| } |
| |
| r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj); |
| if (r < 0) { |
| log_debug_errno(r, "restrict-interfaces: Failed to load BPF object: %m"); |
| return (supported = false); |
| } |
| |
| return (supported = bpf_can_link_program(obj->progs.sd_restrictif_i)); |
| } |
| |
| static int restrict_network_interfaces_install_impl(Unit *u) { |
| _cleanup_(bpf_link_freep) struct bpf_link *egress_link = NULL, *ingress_link = NULL; |
| _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL; |
| _cleanup_free_ char *cgroup_path = NULL; |
| _cleanup_close_ int cgroup_fd = -EBADF; |
| CGroupContext *cc; |
| int r; |
| |
| cc = unit_get_cgroup_context(u); |
| if (!cc) |
| return 0; |
| |
| r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path); |
| if (r < 0) |
| return log_unit_error_errno(u, r, "restrict-interfaces: Failed to get cgroup path: %m"); |
| |
| if (!cc->restrict_network_interfaces) |
| return 0; |
| |
| r = prepare_restrict_ifaces_bpf(u, |
| cc->restrict_network_interfaces_is_allow_list, |
| cc->restrict_network_interfaces, |
| &obj); |
| if (r < 0) |
| return r; |
| |
| cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY, 0); |
| if (cgroup_fd < 0) |
| return -errno; |
| |
| ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd); |
| r = sym_libbpf_get_error(ingress_link); |
| if (r != 0) |
| return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create ingress cgroup link: %m"); |
| |
| egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd); |
| r = sym_libbpf_get_error(egress_link); |
| if (r != 0) |
| return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create egress cgroup link: %m"); |
| |
| u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link); |
| u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link); |
| |
| return 0; |
| } |
| |
| int restrict_network_interfaces_install(Unit *u) { |
| int r = restrict_network_interfaces_install_impl(u); |
| fdset_close(u->initial_restric_ifaces_link_fds); |
| return r; |
| } |
| |
| int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) { |
| int r; |
| |
| assert(u); |
| |
| r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link); |
| if (r < 0) |
| return r; |
| |
| return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link); |
| } |
| |
| int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) { |
| int r; |
| |
| assert(u); |
| |
| if (!u->initial_restric_ifaces_link_fds) { |
| u->initial_restric_ifaces_link_fds = fdset_new(); |
| if (!u->initial_restric_ifaces_link_fds) |
| return log_oom(); |
| } |
| |
| r = fdset_put(u->initial_restric_ifaces_link_fds, fd); |
| if (r < 0) |
| return log_unit_error_errno(u, r, |
| "restrict-interfaces: Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd); |
| |
| return 0; |
| } |
| |
| #else /* ! BPF_FRAMEWORK */ |
| int restrict_network_interfaces_supported(void) { |
| return 0; |
| } |
| |
| int restrict_network_interfaces_install(Unit *u) { |
| return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), |
| "restrict-interfaces: Failed to install; BPF programs built from source code are not supported: %m"); |
| } |
| |
| int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) { |
| return 0; |
| } |
| |
| int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) { |
| return 0; |
| } |
| #endif |