1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "fd-util.h"
4 #include "restrict-ifaces.h"
5 #include "netlink-util.h"
6 
7 #if BPF_FRAMEWORK
8 /* libbpf, clang and llc compile time dependencies are satisfied */
9 
10 #include "bpf-dlopen.h"
11 #include "bpf-link.h"
12 
13 #include "bpf/restrict_ifaces/restrict-ifaces-skel.h"
14 
restrict_ifaces_bpf_free(struct restrict_ifaces_bpf * obj)15 static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) {
16         restrict_ifaces_bpf__destroy(obj);
17         return NULL;
18 }
19 
20 DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_ifaces_bpf *, restrict_ifaces_bpf_free);
21 
prepare_restrict_ifaces_bpf(Unit * u,bool is_allow_list,const Set * restrict_network_interfaces,struct restrict_ifaces_bpf ** ret_object)22 static int prepare_restrict_ifaces_bpf(
23                 Unit* u,
24                 bool is_allow_list,
25                 const Set *restrict_network_interfaces,
26                 struct restrict_ifaces_bpf **ret_object) {
27 
28         _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
29         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
30         char *iface;
31         int r, map_fd;
32 
33         assert(ret_object);
34 
35         obj = restrict_ifaces_bpf__open();
36         if (!obj)
37                 return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "Failed to open BPF object: %m");
38 
39         r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u));
40         if (r != 0)
41                 return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r,
42                                 "Failed to resize BPF map '%s': %m",
43                                 sym_bpf_map__name(obj->maps.sd_restrictif));
44 
45         obj->rodata->is_allow_list = is_allow_list;
46 
47         r = restrict_ifaces_bpf__load(obj);
48         if (r != 0)
49                 return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, r, "Failed to load BPF object: %m");
50 
51         map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif);
52 
53         SET_FOREACH(iface, restrict_network_interfaces) {
54                 uint8_t dummy = 0;
55                 int ifindex;
56 
57                 ifindex = rtnl_resolve_interface(&rtnl, iface);
58                 if (ifindex < 0) {
59                         log_unit_warning_errno(u, ifindex, "Couldn't find index of network interface '%s', ignoring: %m", iface);
60                         continue;
61                 }
62 
63                 if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY))
64                         return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno,
65                                                    "Failed to update BPF map '%s' fd: %m",
66                                                    sym_bpf_map__name(obj->maps.sd_restrictif));
67         }
68 
69         *ret_object = TAKE_PTR(obj);
70         return 0;
71 }
72 
restrict_network_interfaces_supported(void)73 int restrict_network_interfaces_supported(void) {
74         _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
75         static int supported = -1;
76         int r;
77 
78         if (supported >= 0)
79                 return supported;
80 
81         r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
82         if (r < 0)
83                 return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
84         if (r == 0) {
85                 log_debug("Not running with unified cgroup hierarchy, BPF is not supported");
86                 return supported = 0;
87         }
88 
89         if (dlopen_bpf() < 0)
90                 return false;
91 
92         if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) {
93                 log_debug("BPF program type cgroup_skb is not supported");
94                 return supported = 0;
95         }
96 
97         r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj);
98         if (r < 0) {
99                 log_debug_errno(r, "Failed to load BPF object: %m");
100                 return supported = 0;
101         }
102 
103         return supported = bpf_can_link_program(obj->progs.sd_restrictif_i);
104 }
105 
restrict_network_interfaces_install_impl(Unit * u)106 static int restrict_network_interfaces_install_impl(Unit *u) {
107         _cleanup_(bpf_link_freep) struct bpf_link *egress_link = NULL, *ingress_link = NULL;
108         _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
109         _cleanup_free_ char *cgroup_path = NULL;
110         _cleanup_close_ int cgroup_fd = -1;
111         CGroupContext *cc;
112         int r;
113 
114         cc = unit_get_cgroup_context(u);
115         if (!cc)
116                 return 0;
117 
118         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
119         if (r < 0)
120                 return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
121 
122         if (!cc->restrict_network_interfaces)
123                 return 0;
124 
125         r = prepare_restrict_ifaces_bpf(u,
126                 cc->restrict_network_interfaces_is_allow_list,
127                 cc->restrict_network_interfaces,
128                 &obj);
129         if (r < 0)
130                 return r;
131 
132         cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY, 0);
133         if (cgroup_fd < 0)
134                 return -errno;
135 
136         ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd);
137         r = sym_libbpf_get_error(ingress_link);
138         if (r != 0)
139                 return log_unit_error_errno(u, r, "Failed to create ingress cgroup link: %m");
140 
141         egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd);
142         r = sym_libbpf_get_error(egress_link);
143         if (r != 0)
144                 return log_unit_error_errno(u, r, "Failed to create egress cgroup link: %m");
145 
146         u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
147         u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
148 
149         return 0;
150 }
151 
restrict_network_interfaces_install(Unit * u)152 int restrict_network_interfaces_install(Unit *u) {
153         int r = restrict_network_interfaces_install_impl(u);
154         fdset_close(u->initial_restric_ifaces_link_fds);
155         return r;
156 }
157 
serialize_restrict_network_interfaces(Unit * u,FILE * f,FDSet * fds)158 int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
159         int r;
160 
161         assert(u);
162 
163         r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link);
164         if (r < 0)
165                 return r;
166 
167         return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link);
168 }
169 
restrict_network_interfaces_add_initial_link_fd(Unit * u,int fd)170 int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
171         int r;
172 
173         assert(u);
174 
175         if (!u->initial_restric_ifaces_link_fds) {
176                 u->initial_restric_ifaces_link_fds = fdset_new();
177                 if (!u->initial_restric_ifaces_link_fds)
178                         return log_oom();
179         }
180 
181         r = fdset_put(u->initial_restric_ifaces_link_fds, fd);
182         if (r < 0)
183                 return log_unit_error_errno(u, r, "Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd);
184 
185         return 0;
186 }
187 
188 #else /* ! BPF_FRAMEWORK */
restrict_network_interfaces_supported(void)189 int restrict_network_interfaces_supported(void) {
190         return 0;
191 }
192 
restrict_network_interfaces_install(Unit * u)193 int restrict_network_interfaces_install(Unit *u) {
194         return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
195                         "Failed to install RestrictInterfaces: BPF programs built from source code are not supported: %m");
196 }
197 
serialize_restrict_network_interfaces(Unit * u,FILE * f,FDSet * fds)198 int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
199         return 0;
200 }
201 
restrict_network_interfaces_add_initial_link_fd(Unit * u,int fd)202 int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
203         return 0;
204 }
205 #endif
206