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