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