Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <linux/filter.h>
5 : : #include <linux/netlink.h>
6 : : #include <sys/socket.h>
7 : :
8 : : #include "sd-device.h"
9 : : #include "sd-event.h"
10 : :
11 : : #include "MurmurHash2.h"
12 : : #include "alloc-util.h"
13 : : #include "device-monitor-private.h"
14 : : #include "device-private.h"
15 : : #include "device-util.h"
16 : : #include "fd-util.h"
17 : : #include "format-util.h"
18 : : #include "hashmap.h"
19 : : #include "io-util.h"
20 : : #include "missing.h"
21 : : #include "mountpoint-util.h"
22 : : #include "set.h"
23 : : #include "socket-util.h"
24 : : #include "string-util.h"
25 : : #include "strv.h"
26 : :
27 : : struct sd_device_monitor {
28 : : unsigned n_ref;
29 : :
30 : : int sock;
31 : : union sockaddr_union snl;
32 : : union sockaddr_union snl_trusted_sender;
33 : : bool bound;
34 : :
35 : : Hashmap *subsystem_filter;
36 : : Set *tag_filter;
37 : : bool filter_uptodate;
38 : :
39 : : sd_event *event;
40 : : sd_event_source *event_source;
41 : : sd_device_monitor_handler_t callback;
42 : : void *userdata;
43 : : };
44 : :
45 : : #define UDEV_MONITOR_MAGIC 0xfeedcafe
46 : :
47 : : typedef struct monitor_netlink_header {
48 : : /* "libudev" prefix to distinguish libudev and kernel messages */
49 : : char prefix[8];
50 : : /* Magic to protect against daemon <-> Library message format mismatch
51 : : * Used in the kernel from socket filter rules; needs to be stored in network order */
52 : : unsigned magic;
53 : : /* Total length of header structure known to the sender */
54 : : unsigned header_size;
55 : : /* Properties string buffer */
56 : : unsigned properties_off;
57 : : unsigned properties_len;
58 : : /* Hashes of primary device properties strings, to let libudev subscribers
59 : : * use in-kernel socket filters; values need to be stored in network order */
60 : : unsigned filter_subsystem_hash;
61 : : unsigned filter_devtype_hash;
62 : : unsigned filter_tag_bloom_hi;
63 : : unsigned filter_tag_bloom_lo;
64 : : } monitor_netlink_header;
65 : :
66 : 48 : static int monitor_set_nl_address(sd_device_monitor *m) {
67 : : union sockaddr_union snl;
68 : : socklen_t addrlen;
69 : :
70 [ - + ]: 48 : assert(m);
71 : :
72 : : /* Get the address the kernel has assigned us.
73 : : * It is usually, but not necessarily the pid. */
74 : 48 : addrlen = sizeof(struct sockaddr_nl);
75 [ - + ]: 48 : if (getsockname(m->sock, &snl.sa, &addrlen) < 0)
76 : 0 : return -errno;
77 : :
78 : 48 : m->snl.nl.nl_pid = snl.nl.nl_pid;
79 : 48 : return 0;
80 : : }
81 : :
82 : 0 : int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor *sender) {
83 [ # # # # ]: 0 : assert_return(m, -EINVAL);
84 [ # # # # ]: 0 : assert_return(sender, -EINVAL);
85 : :
86 : 0 : m->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid;
87 : 0 : return 0;
88 : : }
89 : :
90 : 44 : _public_ int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size) {
91 : 44 : int r, n = (int) size;
92 : :
93 [ - + - + ]: 44 : assert_return(m, -EINVAL);
94 [ - + - + ]: 44 : assert_return((size_t) n == size, -EINVAL);
95 : :
96 [ + - ]: 44 : if (setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUFFORCE, n) < 0) {
97 : 44 : r = setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUF, n);
98 [ - + ]: 44 : if (r < 0)
99 : 0 : return r;
100 : : }
101 : :
102 : 44 : return 0;
103 : : }
104 : :
105 : 48 : int device_monitor_disconnect(sd_device_monitor *m) {
106 [ - + ]: 48 : assert(m);
107 : :
108 : 48 : m->sock = safe_close(m->sock);
109 : 48 : return 0;
110 : : }
111 : :
112 : 0 : int device_monitor_get_fd(sd_device_monitor *m) {
113 [ # # # # ]: 0 : assert_return(m, -EINVAL);
114 : :
115 : 0 : return m->sock;
116 : : }
117 : :
118 : 48 : int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, int fd) {
119 : 48 : _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
120 : 48 : _cleanup_close_ int sock = -1;
121 : : int r;
122 : :
123 [ - + - + ]: 48 : assert_return(ret, -EINVAL);
124 [ + - - + : 48 : assert_return(group >= 0 && group < _MONITOR_NETLINK_GROUP_MAX, -EINVAL);
- + ]
125 : :
126 [ + - - + ]: 96 : if (group == MONITOR_GROUP_UDEV &&
127 [ # # ]: 48 : access("/run/udev/control", F_OK) < 0 &&
128 : 0 : dev_is_devtmpfs() <= 0) {
129 : :
130 : : /*
131 : : * We do not support subscribing to uevents if no instance of
132 : : * udev is running. Uevents would otherwise broadcast the
133 : : * processing data of the host into containers, which is not
134 : : * desired.
135 : : *
136 : : * Containers will currently not get any udev uevents, until
137 : : * a supporting infrastructure is available.
138 : : *
139 : : * We do not set a netlink multicast group here, so the socket
140 : : * will not receive any messages.
141 : : */
142 : :
143 [ # # ]: 0 : log_debug("sd-device-monitor: The udev service seems not to be active, disabling the monitor");
144 : 0 : group = MONITOR_GROUP_NONE;
145 : : }
146 : :
147 [ + - ]: 48 : if (fd < 0) {
148 : 48 : sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
149 [ - + ]: 48 : if (sock < 0)
150 [ # # ]: 0 : return log_debug_errno(errno, "sd-device-monitor: Failed to create socket: %m");
151 : : }
152 : :
153 : 48 : m = new(sd_device_monitor, 1);
154 [ - + ]: 48 : if (!m)
155 : 0 : return -ENOMEM;
156 : :
157 : 144 : *m = (sd_device_monitor) {
158 : : .n_ref = 1,
159 [ + - ]: 48 : .sock = fd >= 0 ? fd : TAKE_FD(sock),
160 : 48 : .bound = fd >= 0,
161 : : .snl.nl.nl_family = AF_NETLINK,
162 : : .snl.nl.nl_groups = group,
163 : : };
164 : :
165 [ - + ]: 48 : if (fd >= 0) {
166 : 0 : r = monitor_set_nl_address(m);
167 [ # # ]: 0 : if (r < 0)
168 [ # # ]: 0 : return log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
169 : : }
170 : :
171 : 48 : *ret = TAKE_PTR(m);
172 : 48 : return 0;
173 : : }
174 : :
175 : 48 : _public_ int sd_device_monitor_new(sd_device_monitor **ret) {
176 : 48 : return device_monitor_new_full(ret, MONITOR_GROUP_UDEV, -1);
177 : : }
178 : :
179 : 48 : _public_ int sd_device_monitor_stop(sd_device_monitor *m) {
180 [ - + - + ]: 48 : assert_return(m, -EINVAL);
181 : :
182 : 48 : m->event_source = sd_event_source_unref(m->event_source);
183 : 48 : (void) device_monitor_disconnect(m);
184 : :
185 : 48 : return 0;
186 : : }
187 : :
188 : 0 : static int device_monitor_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
189 : 0 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
190 : 0 : sd_device_monitor *m = userdata;
191 : :
192 [ # # ]: 0 : assert(m);
193 : :
194 [ # # ]: 0 : if (device_monitor_receive_device(m, &device) <= 0)
195 : 0 : return 0;
196 : :
197 [ # # ]: 0 : if (m->callback)
198 : 0 : return m->callback(m, device, m->userdata);
199 : :
200 : 0 : return 0;
201 : : }
202 : :
203 : 48 : _public_ int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata) {
204 : : int r;
205 : :
206 [ - + - + ]: 48 : assert_return(m, -EINVAL);
207 : :
208 [ - + ]: 48 : if (!m->event) {
209 : 0 : r = sd_device_monitor_attach_event(m, NULL);
210 [ # # ]: 0 : if (r < 0)
211 : 0 : return r;
212 : : }
213 : :
214 : 48 : r = device_monitor_enable_receiving(m);
215 [ - + ]: 48 : if (r < 0)
216 : 0 : return r;
217 : :
218 : 48 : m->callback = callback;
219 : 48 : m->userdata = userdata;
220 : :
221 : 48 : r = sd_event_add_io(m->event, &m->event_source, m->sock, EPOLLIN, device_monitor_event_handler, m);
222 [ - + ]: 48 : if (r < 0)
223 : 0 : return r;
224 : :
225 : 48 : (void) sd_event_source_set_description(m->event_source, "sd-device-monitor");
226 : :
227 : 48 : return 0;
228 : : }
229 : :
230 : 48 : _public_ int sd_device_monitor_detach_event(sd_device_monitor *m) {
231 [ - + - + ]: 48 : assert_return(m, -EINVAL);
232 : :
233 : 48 : (void) sd_device_monitor_stop(m);
234 : 48 : m->event = sd_event_unref(m->event);
235 : :
236 : 48 : return 0;
237 : : }
238 : :
239 : 48 : _public_ int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event) {
240 : : int r;
241 : :
242 [ - + - + ]: 48 : assert_return(m, -EINVAL);
243 [ - + - + ]: 48 : assert_return(!m->event, -EBUSY);
244 : :
245 [ + - ]: 48 : if (event)
246 : 48 : m->event = sd_event_ref(event);
247 : : else {
248 : 0 : r = sd_event_default(&m->event);
249 [ # # ]: 0 : if (r < 0)
250 : 0 : return r;
251 : : }
252 : :
253 : 48 : return 0;
254 : : }
255 : :
256 : 0 : _public_ sd_event *sd_device_monitor_get_event(sd_device_monitor *m) {
257 [ # # # # ]: 0 : assert_return(m, NULL);
258 : :
259 : 0 : return m->event;
260 : : }
261 : :
262 : 0 : _public_ sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m) {
263 [ # # # # ]: 0 : assert_return(m, NULL);
264 : :
265 : 0 : return m->event_source;
266 : : }
267 : :
268 : 48 : int device_monitor_enable_receiving(sd_device_monitor *m) {
269 : : int r;
270 : :
271 [ - + - + ]: 48 : assert_return(m, -EINVAL);
272 : :
273 : 48 : r = sd_device_monitor_filter_update(m);
274 [ - + ]: 48 : if (r < 0)
275 [ # # ]: 0 : return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
276 : :
277 [ + - ]: 48 : if (!m->bound) {
278 : : /* enable receiving of sender credentials */
279 : 48 : r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true);
280 [ - + ]: 48 : if (r < 0)
281 [ # # ]: 0 : return log_debug_errno(r, "sd-device-monitor: Failed to set socket option SO_PASSCRED: %m");
282 : :
283 [ - + ]: 48 : if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0)
284 [ # # ]: 0 : return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket: %m");
285 : :
286 : 48 : m->bound = true;
287 : :
288 : 48 : r = monitor_set_nl_address(m);
289 [ - + ]: 48 : if (r < 0)
290 [ # # ]: 0 : return log_debug_errno(r, "sd-device-monitor: Failed to set address: %m");
291 : : }
292 : :
293 : 48 : return 0;
294 : : }
295 : :
296 : 48 : static sd_device_monitor *device_monitor_free(sd_device_monitor *m) {
297 [ - + ]: 48 : assert(m);
298 : :
299 : 48 : (void) sd_device_monitor_detach_event(m);
300 : :
301 : 48 : hashmap_free_free_free(m->subsystem_filter);
302 : 48 : set_free_free(m->tag_filter);
303 : :
304 : 48 : return mfree(m);
305 : : }
306 : :
307 [ + + - + : 60 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_monitor, sd_device_monitor, device_monitor_free);
- + ]
308 : :
309 : 0 : static int passes_filter(sd_device_monitor *m, sd_device *device) {
310 : 0 : const char *tag, *subsystem, *devtype, *s, *d = NULL;
311 : : Iterator i;
312 : : int r;
313 : :
314 [ # # # # ]: 0 : assert_return(m, -EINVAL);
315 [ # # # # ]: 0 : assert_return(device, -EINVAL);
316 : :
317 [ # # ]: 0 : if (hashmap_isempty(m->subsystem_filter))
318 : 0 : goto tag;
319 : :
320 : 0 : r = sd_device_get_subsystem(device, &s);
321 [ # # ]: 0 : if (r < 0)
322 : 0 : return r;
323 : :
324 : 0 : r = sd_device_get_devtype(device, &d);
325 [ # # # # ]: 0 : if (r < 0 && r != -ENOENT)
326 : 0 : return r;
327 : :
328 [ # # ]: 0 : HASHMAP_FOREACH_KEY(devtype, subsystem, m->subsystem_filter, i) {
329 [ # # ]: 0 : if (!streq(s, subsystem))
330 : 0 : continue;
331 : :
332 [ # # ]: 0 : if (!devtype)
333 : 0 : goto tag;
334 : :
335 [ # # ]: 0 : if (!d)
336 : 0 : continue;
337 : :
338 [ # # ]: 0 : if (streq(d, devtype))
339 : 0 : goto tag;
340 : : }
341 : :
342 : 0 : return 0;
343 : :
344 : 0 : tag:
345 [ # # ]: 0 : if (set_isempty(m->tag_filter))
346 : 0 : return 1;
347 : :
348 [ # # ]: 0 : SET_FOREACH(tag, m->tag_filter, i)
349 [ # # ]: 0 : if (sd_device_has_tag(device, tag) > 0)
350 : 0 : return 1;
351 : :
352 : 0 : return 0;
353 : : }
354 : :
355 : 0 : int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
356 : 0 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
357 : : union {
358 : : monitor_netlink_header nlh;
359 : : char raw[8192];
360 : : } buf;
361 : 0 : struct iovec iov = {
362 : : .iov_base = &buf,
363 : : .iov_len = sizeof(buf)
364 : : };
365 : : char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
366 : : union sockaddr_union snl;
367 : 0 : struct msghdr smsg = {
368 : : .msg_iov = &iov,
369 : : .msg_iovlen = 1,
370 : : .msg_control = cred_msg,
371 : : .msg_controllen = sizeof(cred_msg),
372 : : .msg_name = &snl,
373 : : .msg_namelen = sizeof(snl),
374 : : };
375 : : struct cmsghdr *cmsg;
376 : : struct ucred *cred;
377 : : ssize_t buflen, bufpos;
378 : 0 : bool is_initialized = false;
379 : : int r;
380 : :
381 [ # # ]: 0 : assert(ret);
382 : :
383 : 0 : buflen = recvmsg(m->sock, &smsg, 0);
384 [ # # ]: 0 : if (buflen < 0) {
385 [ # # ]: 0 : if (errno != EINTR)
386 [ # # ]: 0 : log_debug_errno(errno, "sd-device-monitor: Failed to receive message: %m");
387 : 0 : return -errno;
388 : : }
389 : :
390 [ # # # # ]: 0 : if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC))
391 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
392 : : "sd-device-monitor: Invalid message length.");
393 : :
394 [ # # ]: 0 : if (snl.nl.nl_groups == MONITOR_GROUP_NONE) {
395 : : /* unicast message, check if we trust the sender */
396 [ # # ]: 0 : if (m->snl_trusted_sender.nl.nl_pid == 0 ||
397 [ # # ]: 0 : snl.nl.nl_pid != m->snl_trusted_sender.nl.nl_pid)
398 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
399 : : "sd-device-monitor: Unicast netlink message ignored.");
400 : :
401 [ # # ]: 0 : } else if (snl.nl.nl_groups == MONITOR_GROUP_KERNEL) {
402 [ # # ]: 0 : if (snl.nl.nl_pid > 0)
403 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
404 : : "sd-device-monitor: Multicast kernel netlink message from PID %"PRIu32" ignored.", snl.nl.nl_pid);
405 : : }
406 : :
407 [ # # ]: 0 : cmsg = CMSG_FIRSTHDR(&smsg);
408 [ # # # # ]: 0 : if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
409 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
410 : : "sd-device-monitor: No sender credentials received, message ignored.");
411 : :
412 : 0 : cred = (struct ucred*) CMSG_DATA(cmsg);
413 [ # # ]: 0 : if (cred->uid != 0)
414 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
415 : : "sd-device-monitor: Sender uid="UID_FMT", message ignored.", cred->uid);
416 : :
417 [ # # ]: 0 : if (streq(buf.raw, "libudev")) {
418 : : /* udev message needs proper version magic */
419 [ # # ]: 0 : if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC))
420 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
421 : : "sd-device-monitor: Invalid message signature (%x != %x)",
422 : : buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
423 : :
424 [ # # ]: 0 : if (buf.nlh.properties_off+32 > (size_t) buflen)
425 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
426 : : "sd-device-monitor: Invalid message length (%u > %zd)",
427 : : buf.nlh.properties_off+32, buflen);
428 : :
429 : 0 : bufpos = buf.nlh.properties_off;
430 : :
431 : : /* devices received from udev are always initialized */
432 : 0 : is_initialized = true;
433 : :
434 : : } else {
435 : : /* kernel message with header */
436 : 0 : bufpos = strlen(buf.raw) + 1;
437 [ # # # # ]: 0 : if ((size_t) bufpos < sizeof("a@/d") || bufpos >= buflen)
438 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
439 : : "sd-device-monitor: Invalid message length");
440 : :
441 : : /* check message header */
442 [ # # ]: 0 : if (!strstr(buf.raw, "@/"))
443 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
444 : : "sd-device-monitor: Invalid message header");
445 : : }
446 : :
447 : 0 : r = device_new_from_nulstr(&device, (uint8_t*) &buf.raw[bufpos], buflen - bufpos);
448 [ # # ]: 0 : if (r < 0)
449 [ # # ]: 0 : return log_debug_errno(r, "sd-device-monitor: Failed to create device from received message: %m");
450 : :
451 [ # # ]: 0 : if (is_initialized)
452 : 0 : device_set_is_initialized(device);
453 : :
454 : : /* Skip device, if it does not pass the current filter */
455 : 0 : r = passes_filter(m, device);
456 [ # # ]: 0 : if (r < 0)
457 [ # # # # : 0 : return log_device_debug_errno(device, r, "sd-device-monitor: Failed to check received device passing filter: %m");
# # ]
458 [ # # ]: 0 : if (r == 0)
459 [ # # # # : 0 : log_device_debug(device, "sd-device-monitor: Received device does not pass filter, ignoring");
# # ]
460 : : else
461 : 0 : *ret = TAKE_PTR(device);
462 : :
463 : 0 : return r;
464 : : }
465 : :
466 : 48 : static uint32_t string_hash32(const char *str) {
467 : 48 : return MurmurHash2(str, strlen(str), 0);
468 : : }
469 : :
470 : : /* Get a bunch of bit numbers out of the hash, and set the bits in our bit field */
471 : 44 : static uint64_t string_bloom64(const char *str) {
472 : 44 : uint64_t bits = 0;
473 : 44 : uint32_t hash = string_hash32(str);
474 : :
475 : 44 : bits |= 1LLU << (hash & 63);
476 : 44 : bits |= 1LLU << ((hash >> 6) & 63);
477 : 44 : bits |= 1LLU << ((hash >> 12) & 63);
478 : 44 : bits |= 1LLU << ((hash >> 18) & 63);
479 : 44 : return bits;
480 : : }
481 : :
482 : 0 : int device_monitor_send_device(
483 : : sd_device_monitor *m,
484 : : sd_device_monitor *destination,
485 : : sd_device *device) {
486 : :
487 : 0 : monitor_netlink_header nlh = {
488 : : .prefix = "libudev",
489 : 0 : .magic = htobe32(UDEV_MONITOR_MAGIC),
490 : : .header_size = sizeof nlh,
491 : : };
492 : 0 : struct iovec iov[2] = {
493 : : { .iov_base = &nlh, .iov_len = sizeof nlh },
494 : : };
495 : 0 : struct msghdr smsg = {
496 : : .msg_iov = iov,
497 : : .msg_iovlen = 2,
498 : : };
499 : : /* default destination for sending */
500 : 0 : union sockaddr_union default_destination = {
501 : : .nl.nl_family = AF_NETLINK,
502 : : .nl.nl_groups = MONITOR_GROUP_UDEV,
503 : : };
504 : : uint64_t tag_bloom_bits;
505 : : const char *buf, *val;
506 : : ssize_t count;
507 : : size_t blen;
508 : : int r;
509 : :
510 [ # # ]: 0 : assert(m);
511 [ # # ]: 0 : assert(device);
512 : :
513 : 0 : r = device_get_properties_nulstr(device, (const uint8_t **) &buf, &blen);
514 [ # # ]: 0 : if (r < 0)
515 [ # # # # : 0 : return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device properties: %m");
# # ]
516 [ # # ]: 0 : if (blen < 32) {
517 [ # # # # : 0 : log_device_debug(device, "sd-device-monitor: Length of device property nulstr is too small to contain valid device information");
# # ]
518 : 0 : return -EINVAL;
519 : : }
520 : :
521 : : /* fill in versioned header */
522 : 0 : r = sd_device_get_subsystem(device, &val);
523 [ # # ]: 0 : if (r < 0)
524 [ # # # # : 0 : return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device subsystem: %m");
# # ]
525 : 0 : nlh.filter_subsystem_hash = htobe32(string_hash32(val));
526 : :
527 [ # # ]: 0 : if (sd_device_get_devtype(device, &val) >= 0)
528 : 0 : nlh.filter_devtype_hash = htobe32(string_hash32(val));
529 : :
530 : : /* add tag bloom filter */
531 : 0 : tag_bloom_bits = 0;
532 [ # # ]: 0 : FOREACH_DEVICE_TAG(device, val)
533 : 0 : tag_bloom_bits |= string_bloom64(val);
534 : :
535 [ # # ]: 0 : if (tag_bloom_bits > 0) {
536 : 0 : nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32);
537 : 0 : nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff);
538 : : }
539 : :
540 : : /* add properties list */
541 : 0 : nlh.properties_off = iov[0].iov_len;
542 : 0 : nlh.properties_len = blen;
543 : 0 : iov[1] = IOVEC_MAKE((char*) buf, blen);
544 : :
545 : : /*
546 : : * Use custom address for target, or the default one.
547 : : *
548 : : * If we send to a multicast group, we will get
549 : : * ECONNREFUSED, which is expected.
550 : : */
551 [ # # ]: 0 : smsg.msg_name = destination ? &destination->snl : &default_destination;
552 : 0 : smsg.msg_namelen = sizeof(struct sockaddr_nl);
553 : 0 : count = sendmsg(m->sock, &smsg, 0);
554 [ # # ]: 0 : if (count < 0) {
555 [ # # # # ]: 0 : if (!destination && errno == ECONNREFUSED) {
556 [ # # # # : 0 : log_device_debug(device, "sd-device-monitor: Passed to netlink monitor");
# # ]
557 : 0 : return 0;
558 : : } else
559 [ # # # # : 0 : return log_device_debug_errno(device, errno, "sd-device-monitor: Failed to send device to netlink monitor: %m");
# # ]
560 : : }
561 : :
562 [ # # # # : 0 : log_device_debug(device, "sd-device-monitor: Passed %zi byte to netlink monitor", count);
# # ]
563 : 0 : return count;
564 : : }
565 : :
566 : 376 : static void bpf_stmt(struct sock_filter *ins, unsigned *i,
567 : : unsigned short code, unsigned data) {
568 : 376 : ins[(*i)++] = (struct sock_filter) {
569 : : .code = code,
570 : : .k = data,
571 : : };
572 : 376 : }
573 : :
574 : 140 : static void bpf_jmp(struct sock_filter *ins, unsigned *i,
575 : : unsigned short code, unsigned data,
576 : : unsigned short jt, unsigned short jf) {
577 : 140 : ins[(*i)++] = (struct sock_filter) {
578 : : .code = code,
579 : : .jt = jt,
580 : : .jf = jf,
581 : : .k = data,
582 : : };
583 : 140 : }
584 : :
585 : 48 : _public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
586 : 48 : struct sock_filter ins[512] = {};
587 : : struct sock_fprog filter;
588 : : const char *subsystem, *devtype, *tag;
589 : 48 : unsigned i = 0;
590 : : Iterator it;
591 : :
592 [ - + - + ]: 48 : assert_return(m, -EINVAL);
593 : :
594 [ - + ]: 48 : if (m->filter_uptodate)
595 : 0 : return 0;
596 : :
597 [ + + - + ]: 92 : if (hashmap_isempty(m->subsystem_filter) &&
598 : 44 : set_isempty(m->tag_filter)) {
599 : 0 : m->filter_uptodate = true;
600 : 0 : return 0;
601 : : }
602 : :
603 : : /* load magic in A */
604 : 48 : bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, magic));
605 : : /* jump if magic matches */
606 : 48 : bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
607 : : /* wrong magic, pass packet */
608 : 48 : bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
609 : :
610 [ + + ]: 48 : if (!set_isempty(m->tag_filter)) {
611 : 44 : int tag_matches = set_size(m->tag_filter);
612 : :
613 : : /* add all tags matches */
614 [ + + ]: 88 : SET_FOREACH(tag, m->tag_filter, it) {
615 : 44 : uint64_t tag_bloom_bits = string_bloom64(tag);
616 : 44 : uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
617 : 44 : uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
618 : :
619 : : /* load device bloom bits in A */
620 : 44 : bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_hi));
621 : : /* clear bits (tag bits & bloom bits) */
622 : 44 : bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi);
623 : : /* jump to next tag if it does not match */
624 : 44 : bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3);
625 : :
626 : : /* load device bloom bits in A */
627 : 44 : bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_tag_bloom_lo));
628 : : /* clear bits (tag bits & bloom bits) */
629 : 44 : bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo);
630 : : /* jump behind end of tag match block if tag matches */
631 : 44 : tag_matches--;
632 : 44 : bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0);
633 : : }
634 : :
635 : : /* nothing matched, drop packet */
636 : 44 : bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
637 : : }
638 : :
639 : : /* add all subsystem matches */
640 [ + + ]: 48 : if (!hashmap_isempty(m->subsystem_filter)) {
641 [ + + ]: 8 : HASHMAP_FOREACH_KEY(devtype, subsystem, m->subsystem_filter, it) {
642 : 4 : uint32_t hash = string_hash32(subsystem);
643 : :
644 : : /* load device subsystem value in A */
645 : 4 : bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_subsystem_hash));
646 [ + - ]: 4 : if (!devtype) {
647 : : /* jump if subsystem does not match */
648 : 4 : bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
649 : : } else {
650 : : /* jump if subsystem does not match */
651 : 0 : bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
652 : : /* load device devtype value in A */
653 : 0 : bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_devtype_hash));
654 : : /* jump if value does not match */
655 : 0 : hash = string_hash32(devtype);
656 : 0 : bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
657 : : }
658 : :
659 : : /* matched, pass packet */
660 : 4 : bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
661 : :
662 [ - + ]: 4 : if (i+1 >= ELEMENTSOF(ins))
663 : 0 : return -E2BIG;
664 : : }
665 : :
666 : : /* nothing matched, drop packet */
667 : 4 : bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
668 : : }
669 : :
670 : : /* matched, pass packet */
671 : 48 : bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
672 : :
673 : : /* install filter */
674 : 48 : filter = (struct sock_fprog) {
675 : : .len = i,
676 : : .filter = ins,
677 : : };
678 [ - + ]: 48 : if (setsockopt(m->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
679 : 0 : return -errno;
680 : :
681 : 48 : m->filter_uptodate = true;
682 : 48 : return 0;
683 : : }
684 : :
685 : 4 : _public_ int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype) {
686 : 4 : _cleanup_free_ char *s = NULL, *d = NULL;
687 : : int r;
688 : :
689 [ - + - + ]: 4 : assert_return(m, -EINVAL);
690 [ - + - + ]: 4 : assert_return(subsystem, -EINVAL);
691 : :
692 : 4 : s = strdup(subsystem);
693 [ - + ]: 4 : if (!s)
694 : 0 : return -ENOMEM;
695 : :
696 [ - + ]: 4 : if (devtype) {
697 : 0 : d = strdup(devtype);
698 [ # # ]: 0 : if (!d)
699 : 0 : return -ENOMEM;
700 : : }
701 : :
702 : 4 : r = hashmap_ensure_allocated(&m->subsystem_filter, NULL);
703 [ - + ]: 4 : if (r < 0)
704 : 0 : return r;
705 : :
706 : 4 : r = hashmap_put(m->subsystem_filter, s, d);
707 [ - + ]: 4 : if (r < 0)
708 : 0 : return r;
709 : :
710 : 4 : s = d = NULL;
711 : 4 : m->filter_uptodate = false;
712 : :
713 : 4 : return 0;
714 : : }
715 : :
716 : 44 : _public_ int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag) {
717 : 44 : _cleanup_free_ char *t = NULL;
718 : : int r;
719 : :
720 [ - + - + ]: 44 : assert_return(m, -EINVAL);
721 [ - + - + ]: 44 : assert_return(tag, -EINVAL);
722 : :
723 : 44 : t = strdup(tag);
724 [ - + ]: 44 : if (!t)
725 : 0 : return -ENOMEM;
726 : :
727 : 44 : r = set_ensure_allocated(&m->tag_filter, &string_hash_ops);
728 [ - + ]: 44 : if (r < 0)
729 : 0 : return r;
730 : :
731 : 44 : r = set_put(m->tag_filter, t);
732 [ - + ]: 44 : if (r == -EEXIST)
733 : 0 : return 0;
734 [ - + ]: 44 : if (r < 0)
735 : 0 : return r;
736 : :
737 : 44 : TAKE_PTR(t);
738 : 44 : m->filter_uptodate = false;
739 : :
740 : 44 : return 0;
741 : : }
742 : :
743 : 0 : _public_ int sd_device_monitor_filter_remove(sd_device_monitor *m) {
744 : : static const struct sock_fprog filter = { 0, NULL };
745 : :
746 [ # # # # ]: 0 : assert_return(m, -EINVAL);
747 : :
748 : 0 : m->subsystem_filter = hashmap_free_free_free(m->subsystem_filter);
749 : 0 : m->tag_filter = set_free_free(m->tag_filter);
750 : :
751 [ # # ]: 0 : if (setsockopt(m->sock, SOL_SOCKET, SO_DETACH_FILTER, &filter, sizeof(filter)) < 0)
752 : 0 : return -errno;
753 : :
754 : 0 : m->filter_uptodate = true;
755 : 0 : return 0;
756 : : }
|