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