]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-netlink/netlink-socket.c
Merge pull request #7675 from shawnl/unaligned
[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 const NLTypeSystem *type_system_root;
334
335 assert(rtnl);
336 assert(rtnl->rbuffer);
337 assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
338
339 type_system_root = type_system_get_root(rtnl->protocol);
340
341 /* read nothing, just get the pending message size */
342 r = socket_recv_message(rtnl->fd, &iov, NULL, true);
343 if (r <= 0)
344 return r;
345 else
346 len = (size_t)r;
347
348 /* make room for the pending message */
349 if (!greedy_realloc((void **)&rtnl->rbuffer,
350 &rtnl->rbuffer_allocated,
351 len, sizeof(uint8_t)))
352 return -ENOMEM;
353
354 iov.iov_base = rtnl->rbuffer;
355 iov.iov_len = rtnl->rbuffer_allocated;
356
357 /* read the pending message */
358 r = socket_recv_message(rtnl->fd, &iov, &group, false);
359 if (r <= 0)
360 return r;
361 else
362 len = (size_t)r;
363
364 if (len > rtnl->rbuffer_allocated)
365 /* message did not fit in read buffer */
366 return -EIO;
367
368 if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
369 multi_part = true;
370
371 for (i = 0; i < rtnl->rqueue_partial_size; i++) {
372 if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
373 rtnl->rbuffer->nlmsg_seq) {
374 first = rtnl->rqueue_partial[i];
375 break;
376 }
377 }
378 }
379
380 for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
381 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
382 const NLType *nl_type;
383
384 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
385 /* not broadcast and not for us */
386 continue;
387
388 if (new_msg->nlmsg_type == NLMSG_NOOP)
389 /* silently drop noop messages */
390 continue;
391
392 if (new_msg->nlmsg_type == NLMSG_DONE) {
393 /* finished reading multi-part message */
394 done = true;
395
396 /* if first is not defined, put NLMSG_DONE into the receive queue. */
397 if (first)
398 continue;
399 }
400
401 /* check that we support this message type */
402 r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
403
404 if (r < 0) {
405 if (r == -EOPNOTSUPP)
406 log_debug("sd-netlink: ignored message with unknown type: %i",
407 new_msg->nlmsg_type);
408
409 continue;
410 }
411
412 /* check that the size matches the message type */
413 if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
414 log_debug("sd-netlink: message larger than expected, dropping");
415 continue;
416 }
417
418 r = message_new_empty(rtnl, &m);
419 if (r < 0)
420 return r;
421
422 m->broadcast = !!group;
423
424 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
425 if (!m->hdr)
426 return -ENOMEM;
427
428 /* seal and parse the top-level message */
429 r = sd_netlink_message_rewind(m);
430 if (r < 0)
431 return r;
432
433 /* push the message onto the multi-part message stack */
434 if (first)
435 m->next = first;
436 first = m;
437 m = NULL;
438 }
439
440 if (len)
441 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
442
443 if (!first)
444 return 0;
445
446 if (!multi_part || done) {
447 /* we got a complete message, push it on the read queue */
448 r = rtnl_rqueue_make_room(rtnl);
449 if (r < 0)
450 return r;
451
452 rtnl->rqueue[rtnl->rqueue_size++] = first;
453 first = NULL;
454
455 if (multi_part && (i < rtnl->rqueue_partial_size)) {
456 /* remove the message form the partial read queue */
457 memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
458 sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
459 rtnl->rqueue_partial_size--;
460 }
461
462 return 1;
463 } else {
464 /* we only got a partial multi-part message, push it on the
465 partial read queue */
466 if (i < rtnl->rqueue_partial_size) {
467 rtnl->rqueue_partial[i] = first;
468 } else {
469 r = rtnl_rqueue_partial_make_room(rtnl);
470 if (r < 0)
471 return r;
472
473 rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = first;
474 }
475 first = NULL;
476
477 return 0;
478 }
479 }