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