Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <netinet/in.h>
4 : : #include <stdbool.h>
5 : : #include <unistd.h>
6 : :
7 : : #include "sd-netlink.h"
8 : :
9 : : #include "alloc-util.h"
10 : : #include "fd-util.h"
11 : : #include "format-util.h"
12 : : #include "io-util.h"
13 : : #include "missing.h"
14 : : #include "netlink-internal.h"
15 : : #include "netlink-types.h"
16 : : #include "netlink-util.h"
17 : : #include "socket-util.h"
18 : : #include "util.h"
19 : :
20 : 60 : int socket_open(int family) {
21 : : int fd;
22 : :
23 : 60 : fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
24 [ - + ]: 60 : if (fd < 0)
25 : 0 : return -errno;
26 : :
27 : 60 : return fd_move_above_stdio(fd);
28 : : }
29 : :
30 : 60 : static int broadcast_groups_get(sd_netlink *nl) {
31 : 60 : _cleanup_free_ uint32_t *groups = NULL;
32 : 60 : socklen_t len = 0, old_len;
33 : : unsigned i, j;
34 : : int r;
35 : :
36 [ - + ]: 60 : assert(nl);
37 [ - + ]: 60 : assert(nl->fd >= 0);
38 : :
39 : 60 : r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
40 [ - + ]: 60 : if (r < 0) {
41 [ # # ]: 0 : if (errno == ENOPROTOOPT) {
42 : 0 : nl->broadcast_group_dont_leave = true;
43 : 0 : return 0;
44 : : } else
45 : 0 : return -errno;
46 : : }
47 : :
48 [ + - ]: 60 : if (len == 0)
49 : 60 : return 0;
50 : :
51 [ # # ]: 0 : groups = new0(uint32_t, len);
52 [ # # ]: 0 : if (!groups)
53 : 0 : return -ENOMEM;
54 : :
55 : 0 : old_len = len;
56 : :
57 : 0 : r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len);
58 [ # # ]: 0 : if (r < 0)
59 : 0 : return -errno;
60 : :
61 [ # # ]: 0 : if (old_len != len)
62 : 0 : return -EIO;
63 : :
64 : 0 : r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
65 [ # # ]: 0 : if (r < 0)
66 : 0 : return r;
67 : :
68 [ # # ]: 0 : for (i = 0; i < len; i++) {
69 [ # # ]: 0 : for (j = 0; j < sizeof(uint32_t) * 8; j++) {
70 : : uint32_t offset;
71 : : unsigned group;
72 : :
73 : 0 : offset = 1U << j;
74 : :
75 [ # # ]: 0 : if (!(groups[i] & offset))
76 : 0 : continue;
77 : :
78 : 0 : group = i * sizeof(uint32_t) * 8 + j + 1;
79 : :
80 : 0 : r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1));
81 [ # # ]: 0 : if (r < 0)
82 : 0 : return r;
83 : : }
84 : : }
85 : :
86 : 0 : return 0;
87 : : }
88 : :
89 : 60 : int socket_bind(sd_netlink *nl) {
90 : : socklen_t addrlen;
91 : : int r;
92 : :
93 : 60 : r = setsockopt_int(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, true);
94 [ - + ]: 60 : if (r < 0)
95 : 0 : return r;
96 : :
97 : 60 : addrlen = sizeof(nl->sockaddr);
98 : :
99 : 60 : r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
100 : : /* ignore EINVAL to allow opening an already bound socket */
101 [ - + # # ]: 60 : if (r < 0 && errno != EINVAL)
102 : 0 : return -errno;
103 : :
104 : 60 : r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
105 [ - + ]: 60 : if (r < 0)
106 : 0 : return -errno;
107 : :
108 : 60 : r = broadcast_groups_get(nl);
109 [ - + ]: 60 : if (r < 0)
110 : 0 : return r;
111 : :
112 : 60 : return 0;
113 : : }
114 : :
115 : 128 : static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
116 [ - + ]: 128 : assert(nl);
117 : :
118 : 128 : return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
119 : : }
120 : :
121 : 128 : static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
122 : : int r;
123 : :
124 [ - + ]: 128 : assert(nl);
125 : :
126 : 128 : r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
127 [ - + ]: 128 : if (r < 0)
128 : 0 : return r;
129 : :
130 : 128 : return 0;
131 : : }
132 : :
133 : 36 : static int broadcast_group_join(sd_netlink *nl, unsigned group) {
134 : : int r;
135 : :
136 [ - + ]: 36 : assert(nl);
137 [ - + ]: 36 : assert(nl->fd >= 0);
138 [ - + ]: 36 : assert(group > 0);
139 : :
140 : 36 : r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
141 [ - + ]: 36 : if (r < 0)
142 : 0 : return -errno;
143 : :
144 : 36 : return 0;
145 : : }
146 : :
147 : 76 : int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
148 : : unsigned n_ref;
149 : : int r;
150 : :
151 [ - + ]: 76 : assert(nl);
152 : :
153 : 76 : n_ref = broadcast_group_get_ref(nl, group);
154 : :
155 : 76 : n_ref++;
156 : :
157 : 76 : r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
158 [ - + ]: 76 : if (r < 0)
159 : 0 : return r;
160 : :
161 : 76 : r = broadcast_group_set_ref(nl, group, n_ref);
162 [ - + ]: 76 : if (r < 0)
163 : 0 : return r;
164 : :
165 [ + + ]: 76 : if (n_ref > 1)
166 : : /* not yet in the group */
167 : 40 : return 0;
168 : :
169 : 36 : r = broadcast_group_join(nl, group);
170 [ - + ]: 36 : if (r < 0)
171 : 0 : return r;
172 : :
173 : 36 : return 0;
174 : : }
175 : :
176 : 24 : static int broadcast_group_leave(sd_netlink *nl, unsigned group) {
177 : : int r;
178 : :
179 [ - + ]: 24 : assert(nl);
180 [ - + ]: 24 : assert(nl->fd >= 0);
181 [ - + ]: 24 : assert(group > 0);
182 : :
183 [ - + ]: 24 : if (nl->broadcast_group_dont_leave)
184 : 0 : return 0;
185 : :
186 : 24 : r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
187 [ - + ]: 24 : if (r < 0)
188 : 0 : return -errno;
189 : :
190 : 24 : return 0;
191 : : }
192 : :
193 : 52 : int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
194 : : unsigned n_ref;
195 : : int r;
196 : :
197 [ - + ]: 52 : assert(nl);
198 : :
199 : 52 : n_ref = broadcast_group_get_ref(nl, group);
200 : :
201 [ - + ]: 52 : assert(n_ref > 0);
202 : :
203 : 52 : n_ref--;
204 : :
205 : 52 : r = broadcast_group_set_ref(nl, group, n_ref);
206 [ - + ]: 52 : if (r < 0)
207 : 0 : return r;
208 : :
209 [ + + ]: 52 : if (n_ref > 0)
210 : : /* still refs left */
211 : 28 : return 0;
212 : :
213 : 24 : r = broadcast_group_leave(nl, group);
214 [ - + ]: 24 : if (r < 0)
215 : 0 : return r;
216 : :
217 : 24 : return 0;
218 : : }
219 : :
220 : : /* returns the number of bytes sent, or a negative error code */
221 : 108 : int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
222 : : union {
223 : : struct sockaddr sa;
224 : : struct sockaddr_nl nl;
225 : 108 : } addr = {
226 : : .nl.nl_family = AF_NETLINK,
227 : : };
228 : : ssize_t k;
229 : :
230 [ - + ]: 108 : assert(nl);
231 [ - + ]: 108 : assert(m);
232 [ - + ]: 108 : assert(m->hdr);
233 : :
234 : 108 : k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
235 : : 0, &addr.sa, sizeof(addr));
236 [ - + ]: 108 : if (k < 0)
237 : 0 : return -errno;
238 : :
239 : 108 : return k;
240 : : }
241 : :
242 : 256 : static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
243 : : union sockaddr_union sender;
244 : : uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
245 : 256 : struct msghdr msg = {
246 : : .msg_iov = iov,
247 : : .msg_iovlen = 1,
248 : : .msg_name = &sender,
249 : : .msg_namelen = sizeof(sender),
250 : : .msg_control = cmsg_buffer,
251 : : .msg_controllen = sizeof(cmsg_buffer),
252 : : };
253 : : struct cmsghdr *cmsg;
254 : 256 : uint32_t group = 0;
255 : : ssize_t n;
256 : :
257 [ - + ]: 256 : assert(fd >= 0);
258 [ - + ]: 256 : assert(iov);
259 : :
260 [ + + ]: 256 : n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
261 [ - + ]: 256 : if (n < 0) {
262 : : /* no data */
263 [ # # ]: 0 : if (errno == ENOBUFS)
264 [ # # ]: 0 : log_debug("rtnl: kernel receive buffer overrun");
265 [ # # ]: 0 : else if (errno == EAGAIN)
266 [ # # ]: 0 : log_debug("rtnl: no data in socket");
267 : :
268 [ # # # # ]: 0 : return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
269 : : }
270 : :
271 [ - + ]: 256 : if (sender.nl.nl_pid != 0) {
272 : : /* not from the kernel, ignore */
273 [ # # ]: 0 : log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
274 : :
275 [ # # ]: 0 : if (peek) {
276 : : /* drop the message */
277 : 0 : n = recvmsg(fd, &msg, 0);
278 [ # # ]: 0 : if (n < 0)
279 [ # # # # ]: 0 : return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
280 : : }
281 : :
282 : 0 : return 0;
283 : : }
284 : :
285 [ + - + + ]: 512 : CMSG_FOREACH(cmsg, &msg) {
286 [ + - ]: 256 : if (cmsg->cmsg_level == SOL_NETLINK &&
287 [ + - ]: 256 : cmsg->cmsg_type == NETLINK_PKTINFO &&
288 [ + - ]: 256 : cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
289 : 256 : struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
290 : :
291 : : /* multi-cast group */
292 : 256 : group = pktinfo->group;
293 : : }
294 : : }
295 : :
296 [ + + ]: 256 : if (_group)
297 : 128 : *_group = group;
298 : :
299 : 256 : return (int) n;
300 : : }
301 : :
302 : : /* On success, the number of bytes received is returned and *ret points to the received message
303 : : * which has a valid header and the correct size.
304 : : * If nothing useful was received 0 is returned.
305 : : * On failure, a negative error code is returned.
306 : : */
307 : 128 : int socket_read_message(sd_netlink *rtnl) {
308 : 128 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
309 : 128 : struct iovec iov = {};
310 : 128 : uint32_t group = 0;
311 : 128 : bool multi_part = false, done = false;
312 : : struct nlmsghdr *new_msg;
313 : : size_t len;
314 : : int r;
315 : 128 : unsigned i = 0;
316 : : const NLTypeSystem *type_system_root;
317 : :
318 [ - + ]: 128 : assert(rtnl);
319 [ - + ]: 128 : assert(rtnl->rbuffer);
320 [ - + ]: 128 : assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
321 : :
322 : 128 : type_system_root = type_system_get_root(rtnl->protocol);
323 : :
324 : : /* read nothing, just get the pending message size */
325 : 128 : r = socket_recv_message(rtnl->fd, &iov, NULL, true);
326 [ - + ]: 128 : if (r <= 0)
327 : 0 : return r;
328 : : else
329 : 128 : len = (size_t) r;
330 : :
331 : : /* make room for the pending message */
332 [ - + ]: 128 : if (!greedy_realloc((void **)&rtnl->rbuffer,
333 : : &rtnl->rbuffer_allocated,
334 : : len, sizeof(uint8_t)))
335 : 0 : return -ENOMEM;
336 : :
337 : 128 : iov = IOVEC_MAKE(rtnl->rbuffer, rtnl->rbuffer_allocated);
338 : :
339 : : /* read the pending message */
340 : 128 : r = socket_recv_message(rtnl->fd, &iov, &group, false);
341 [ - + ]: 128 : if (r <= 0)
342 : 0 : return r;
343 : : else
344 : 128 : len = (size_t) r;
345 : :
346 [ - + ]: 128 : if (len > rtnl->rbuffer_allocated)
347 : : /* message did not fit in read buffer */
348 : 0 : return -EIO;
349 : :
350 [ + - + - : 128 : if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
+ - + + ]
351 : 60 : multi_part = true;
352 : :
353 [ + + ]: 60 : for (i = 0; i < rtnl->rqueue_partial_size; i++) {
354 : 40 : if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
355 [ + - ]: 40 : rtnl->rbuffer->nlmsg_seq) {
356 : 40 : first = rtnl->rqueue_partial[i];
357 : 40 : break;
358 : : }
359 : : }
360 : : }
361 : :
362 [ + + + - : 472 : for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
+ - + - ]
363 [ + + - ]: 344 : _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
364 : : const NLType *nl_type;
365 : :
366 [ + - - + ]: 344 : if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
367 : : /* not broadcast and not for us */
368 : 0 : continue;
369 : :
370 [ - + ]: 344 : if (new_msg->nlmsg_type == NLMSG_NOOP)
371 : : /* silently drop noop messages */
372 : 0 : continue;
373 : :
374 [ + + ]: 344 : if (new_msg->nlmsg_type == NLMSG_DONE) {
375 : : /* finished reading multi-part message */
376 : 20 : done = true;
377 : :
378 : : /* if first is not defined, put NLMSG_DONE into the receive queue. */
379 [ + - ]: 20 : if (first)
380 : 20 : continue;
381 : : }
382 : :
383 : : /* check that we support this message type */
384 : 324 : r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
385 [ - + ]: 324 : if (r < 0) {
386 [ # # ]: 0 : if (r == -EOPNOTSUPP)
387 [ # # ]: 0 : log_debug("sd-netlink: ignored message with unknown type: %i",
388 : : new_msg->nlmsg_type);
389 : :
390 : 0 : continue;
391 : : }
392 : :
393 : : /* check that the size matches the message type */
394 [ - + ]: 324 : if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
395 [ # # ]: 0 : log_debug("sd-netlink: message is shorter than expected, dropping");
396 : 0 : continue;
397 : : }
398 : :
399 : 324 : r = message_new_empty(rtnl, &m);
400 [ - + ]: 324 : if (r < 0)
401 : 0 : return r;
402 : :
403 : 324 : m->broadcast = !!group;
404 : :
405 : 324 : m->hdr = memdup(new_msg, new_msg->nlmsg_len);
406 [ - + ]: 324 : if (!m->hdr)
407 : 0 : return -ENOMEM;
408 : :
409 : : /* seal and parse the top-level message */
410 : 324 : r = sd_netlink_message_rewind(m);
411 [ - + ]: 324 : if (r < 0)
412 : 0 : return r;
413 : :
414 : : /* push the message onto the multi-part message stack */
415 [ + + ]: 324 : if (first)
416 : 236 : m->next = first;
417 : 324 : first = TAKE_PTR(m);
418 : : }
419 : :
420 [ - + ]: 128 : if (len > 0)
421 [ # # ]: 0 : log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
422 : :
423 [ - + ]: 128 : if (!first)
424 : 0 : return 0;
425 : :
426 [ + + + + ]: 128 : if (!multi_part || done) {
427 : : /* we got a complete message, push it on the read queue */
428 : 88 : r = rtnl_rqueue_make_room(rtnl);
429 [ - + ]: 88 : if (r < 0)
430 : 0 : return r;
431 : :
432 : 88 : rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
433 : :
434 [ + + + - ]: 88 : if (multi_part && (i < rtnl->rqueue_partial_size)) {
435 : : /* remove the message form the partial read queue */
436 : 20 : memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
437 : 20 : sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
438 : 20 : rtnl->rqueue_partial_size--;
439 : : }
440 : :
441 : 88 : return 1;
442 : : } else {
443 : : /* we only got a partial multi-part message, push it on the
444 : : partial read queue */
445 [ + + ]: 40 : if (i < rtnl->rqueue_partial_size)
446 : 20 : rtnl->rqueue_partial[i] = TAKE_PTR(first);
447 : : else {
448 : 20 : r = rtnl_rqueue_partial_make_room(rtnl);
449 [ - + ]: 20 : if (r < 0)
450 : 0 : return r;
451 : :
452 : 20 : rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
453 : : }
454 : :
455 : 40 : return 0;
456 : : }
457 : : }
|