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