]>
Commit | Line | Data |
---|---|---|
948def4a | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
6f50d4f7 MV |
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" | |
bb0b01ed | 12 | #include "bpf-util.h" |
d40ce018 | 13 | #include "bpf/restrict_ifaces/restrict-ifaces-skel.h" |
6f50d4f7 MV |
14 | |
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 | ||
cc8943b8 YW |
22 | static int prepare_restrict_ifaces_bpf( |
23 | Unit* u, | |
24 | bool is_allow_list, | |
6f50d4f7 MV |
25 | const Set *restrict_network_interfaces, |
26 | struct restrict_ifaces_bpf **ret_object) { | |
cc8943b8 | 27 | |
6f50d4f7 MV |
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) | |
b1acbc08 | 37 | return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "restrict-interfaces: Failed to open BPF object: %m"); |
6f50d4f7 | 38 | |
6b8085db | 39 | r = sym_bpf_map__set_max_entries(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u)); |
6f50d4f7 | 40 | if (r != 0) |
8f048bb7 | 41 | return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, |
b1acbc08 | 42 | "restrict-interfaces: Failed to resize BPF map '%s': %m", |
6f50d4f7 MV |
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) | |
b1acbc08 | 49 | return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, r, "restrict-interfaces: Failed to load BPF object: %m"); |
6f50d4f7 MV |
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; | |
cc8943b8 | 56 | |
6f50d4f7 MV |
57 | ifindex = rtnl_resolve_interface(&rtnl, iface); |
58 | if (ifindex < 0) { | |
b1acbc08 ZJS |
59 | log_unit_warning_errno(u, ifindex, |
60 | "restrict-interfaces: Couldn't find index of network interface '%s', ignoring: %m", | |
61 | iface); | |
6f50d4f7 MV |
62 | continue; |
63 | } | |
64 | ||
65 | if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY)) | |
8f048bb7 | 66 | return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, |
b1acbc08 | 67 | "restrict-interfaces: Failed to update BPF map '%s' fd: %m", |
8f048bb7 | 68 | sym_bpf_map__name(obj->maps.sd_restrictif)); |
6f50d4f7 MV |
69 | } |
70 | ||
71 | *ret_object = TAKE_PTR(obj); | |
72 | return 0; | |
73 | } | |
74 | ||
75 | int restrict_network_interfaces_supported(void) { | |
76 | _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL; | |
6f50d4f7 | 77 | static int supported = -1; |
3de3fd3d | 78 | int r; |
6f50d4f7 MV |
79 | |
80 | if (supported >= 0) | |
81 | return supported; | |
82 | ||
bb0b01ed ZJS |
83 | if (!cgroup_bpf_supported()) |
84 | return (supported = false); | |
6f50d4f7 | 85 | |
87e462f7 | 86 | if (!compat_libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*opts=*/NULL)) { |
b1acbc08 | 87 | log_debug("restrict-interfaces: BPF program type cgroup_skb is not supported"); |
bb0b01ed | 88 | return (supported = false); |
6f50d4f7 MV |
89 | } |
90 | ||
91 | r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj); | |
3de3fd3d | 92 | if (r < 0) { |
b1acbc08 | 93 | log_debug_errno(r, "restrict-interfaces: Failed to load BPF object: %m"); |
bb0b01ed | 94 | return (supported = false); |
3de3fd3d | 95 | } |
6f50d4f7 | 96 | |
bb0b01ed | 97 | return (supported = bpf_can_link_program(obj->progs.sd_restrictif_i)); |
6f50d4f7 MV |
98 | } |
99 | ||
100 | static int restrict_network_interfaces_install_impl(Unit *u) { | |
101 | _cleanup_(bpf_link_freep) struct bpf_link *egress_link = NULL, *ingress_link = NULL; | |
102 | _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL; | |
103 | _cleanup_free_ char *cgroup_path = NULL; | |
254d1313 | 104 | _cleanup_close_ int cgroup_fd = -EBADF; |
6f50d4f7 MV |
105 | CGroupContext *cc; |
106 | int r; | |
107 | ||
108 | cc = unit_get_cgroup_context(u); | |
109 | if (!cc) | |
110 | return 0; | |
111 | ||
112 | r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path); | |
113 | if (r < 0) | |
b1acbc08 | 114 | return log_unit_error_errno(u, r, "restrict-interfaces: Failed to get cgroup path: %m"); |
6f50d4f7 MV |
115 | |
116 | if (!cc->restrict_network_interfaces) | |
117 | return 0; | |
118 | ||
119 | r = prepare_restrict_ifaces_bpf(u, | |
120 | cc->restrict_network_interfaces_is_allow_list, | |
121 | cc->restrict_network_interfaces, | |
122 | &obj); | |
123 | if (r < 0) | |
124 | return r; | |
125 | ||
126 | cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY, 0); | |
127 | if (cgroup_fd < 0) | |
128 | return -errno; | |
129 | ||
130 | ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd); | |
131 | r = sym_libbpf_get_error(ingress_link); | |
132 | if (r != 0) | |
b1acbc08 | 133 | return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create ingress cgroup link: %m"); |
6f50d4f7 MV |
134 | |
135 | egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd); | |
136 | r = sym_libbpf_get_error(egress_link); | |
137 | if (r != 0) | |
b1acbc08 | 138 | return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create egress cgroup link: %m"); |
6f50d4f7 MV |
139 | |
140 | u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link); | |
141 | u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | int restrict_network_interfaces_install(Unit *u) { | |
147 | int r = restrict_network_interfaces_install_impl(u); | |
148 | fdset_close(u->initial_restric_ifaces_link_fds); | |
149 | return r; | |
150 | } | |
151 | ||
152 | int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) { | |
153 | int r; | |
154 | ||
155 | assert(u); | |
156 | ||
157 | r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link); | |
158 | if (r < 0) | |
159 | return r; | |
160 | ||
161 | return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link); | |
162 | } | |
163 | ||
164 | int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) { | |
165 | int r; | |
166 | ||
167 | assert(u); | |
168 | ||
169 | if (!u->initial_restric_ifaces_link_fds) { | |
170 | u->initial_restric_ifaces_link_fds = fdset_new(); | |
171 | if (!u->initial_restric_ifaces_link_fds) | |
172 | return log_oom(); | |
173 | } | |
174 | ||
175 | r = fdset_put(u->initial_restric_ifaces_link_fds, fd); | |
176 | if (r < 0) | |
b1acbc08 ZJS |
177 | return log_unit_error_errno(u, r, |
178 | "restrict-interfaces: Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd); | |
6f50d4f7 MV |
179 | |
180 | return 0; | |
181 | } | |
182 | ||
183 | #else /* ! BPF_FRAMEWORK */ | |
184 | int restrict_network_interfaces_supported(void) { | |
185 | return 0; | |
186 | } | |
187 | ||
188 | int restrict_network_interfaces_install(Unit *u) { | |
189 | return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), | |
b1acbc08 | 190 | "restrict-interfaces: Failed to install; BPF programs built from source code are not supported: %m"); |
6f50d4f7 MV |
191 | } |
192 | ||
193 | int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) { | |
194 | return 0; | |
195 | } | |
196 | ||
197 | int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) { | |
198 | return 0; | |
199 | } | |
200 | #endif |