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