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