1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "sd-netlink.h"
5 #include "alloc-util.h"
6 #include "errno-util.h"
8 #include "iovec-util.h"
10 #include "netlink-internal.h"
11 #include "netlink-types.h"
12 #include "ordered-set.h"
13 #include "socket-util.h"
15 static int broadcast_groups_get(sd_netlink
*nl
) {
16 _cleanup_free_
uint32_t *groups
= NULL
;
23 r
= netlink_socket_get_multicast_groups(nl
->fd
, &len
, &groups
);
27 for (size_t i
= 0; i
< len
; i
++)
28 for (unsigned j
= 0; j
< sizeof(uint32_t) * 8; j
++)
29 if (groups
[i
] & (1U << j
)) {
30 unsigned group
= i
* sizeof(uint32_t) * 8 + j
+ 1;
32 r
= hashmap_ensure_put(&nl
->broadcast_group_refs
, NULL
, UINT_TO_PTR(group
), UINT_TO_PTR(1));
40 int socket_bind(sd_netlink
*nl
) {
44 r
= setsockopt_int(nl
->fd
, SOL_NETLINK
, NETLINK_PKTINFO
, true);
48 addrlen
= sizeof(nl
->sockaddr
);
50 /* ignore EINVAL to allow binding an already bound socket */
51 if (bind(nl
->fd
, &nl
->sockaddr
.sa
, addrlen
) < 0 && errno
!= EINVAL
)
54 if (getsockname(nl
->fd
, &nl
->sockaddr
.sa
, &addrlen
) < 0)
57 return broadcast_groups_get(nl
);
60 static unsigned broadcast_group_get_ref(sd_netlink
*nl
, unsigned group
) {
63 return PTR_TO_UINT(hashmap_get(nl
->broadcast_group_refs
, UINT_TO_PTR(group
)));
66 static int broadcast_group_set_ref(sd_netlink
*nl
, unsigned group
, unsigned n_ref
) {
69 return hashmap_ensure_replace(&nl
->broadcast_group_refs
, NULL
, UINT_TO_PTR(group
), UINT_TO_PTR(n_ref
));
72 static int broadcast_group_join(sd_netlink
*nl
, unsigned group
) {
77 /* group is "unsigned", but netlink(7) says the argument for NETLINK_ADD_MEMBERSHIP is "int" */
78 return setsockopt_int(nl
->fd
, SOL_NETLINK
, NETLINK_ADD_MEMBERSHIP
, group
);
81 int socket_broadcast_group_ref(sd_netlink
*nl
, unsigned group
) {
87 n_ref
= broadcast_group_get_ref(nl
, group
);
91 r
= broadcast_group_set_ref(nl
, group
, n_ref
);
96 /* already in the group */
99 return broadcast_group_join(nl
, group
);
102 int socket_broadcast_group_unref(sd_netlink
*nl
, unsigned group
) {
108 n_ref
= broadcast_group_get_ref(nl
, group
);
114 r
= broadcast_group_set_ref(nl
, group
, n_ref
);
119 /* still refs left */
122 /* group is "unsigned", but netlink(7) says the argument for NETLINK_DROP_MEMBERSHIP is "int" */
123 return setsockopt_int(nl
->fd
, SOL_NETLINK
, NETLINK_DROP_MEMBERSHIP
, group
);
126 /* returns the number of bytes sent, or a negative error code */
127 int socket_write_message(sd_netlink
*nl
, sd_netlink_message
*m
) {
128 union sockaddr_union addr
= {
129 .nl
.nl_family
= AF_NETLINK
,
137 k
= sendto(nl
->fd
, m
->hdr
, m
->hdr
->nlmsg_len
, 0, &addr
.sa
, sizeof(addr
));
144 static int socket_recv_message(int fd
, void *buf
, size_t buf_size
, uint32_t *ret_mcast_group
, bool peek
) {
145 struct iovec iov
= IOVEC_MAKE(buf
, buf_size
);
146 union sockaddr_union sender
;
147 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo
))) control
;
148 struct msghdr msg
= {
152 .msg_namelen
= sizeof(sender
),
153 .msg_control
= &control
,
154 .msg_controllen
= sizeof(control
),
159 assert(peek
|| (buf
&& buf_size
> 0));
161 /* Note: this might return successfully, but with a zero size under some transient conditions, such
162 * as the reception of a non-kernel message. In such a case the passed buffer might or might not be
163 * modified. Caller must treat a zero return as "no message, but also not an error". */
165 n
= recvmsg_safe(fd
, &msg
, peek
? (MSG_PEEK
|MSG_TRUNC
) : 0);
166 if (ERRNO_IS_NEG_TRANSIENT(n
))
169 return log_debug_errno(n
, "sd-netlink: kernel receive buffer overrun");
171 return log_debug_errno(n
, "sd-netlink: got truncated control message");
173 return log_debug_errno(n
, "sd-netlink: got truncated payload message");
177 if (sender
.nl
.nl_pid
!= 0) {
178 /* not from the kernel, ignore */
179 log_debug("sd-netlink: ignoring message from PID %"PRIu32
, sender
.nl
.nl_pid
);
182 /* Drop the message. Note that we ignore ECHRNG/EXFULL errors here, which
183 * recvmsg_safe() returns in case the payload or cdata is truncated. Given we just
184 * want to drop the message we also don't care if its payload or cdata was
186 n
= recvmsg_safe(fd
, &msg
, 0);
187 if (n
< 0 && !IN_SET(n
, -ECHRNG
, -EXFULL
))
194 if (ret_mcast_group
) {
195 struct nl_pktinfo
*pi
;
197 pi
= CMSG_FIND_DATA(&msg
, SOL_NETLINK
, NETLINK_PKTINFO
, struct nl_pktinfo
);
199 *ret_mcast_group
= pi
->group
;
201 *ret_mcast_group
= 0;
208 *ret_mcast_group
= 0;
213 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
214 netlink_message_hash_ops
,
215 void, trivial_hash_func
, trivial_compare_func
,
216 sd_netlink_message
, sd_netlink_message_unref
);
218 static int netlink_queue_received_message(sd_netlink
*nl
, sd_netlink_message
*m
) {
225 if (ordered_set_size(nl
->rqueue
) >= NETLINK_RQUEUE_MAX
)
226 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS
),
227 "sd-netlink: exhausted the read queue size (%d)", NETLINK_RQUEUE_MAX
);
229 r
= ordered_set_ensure_put(&nl
->rqueue
, &netlink_message_hash_ops
, m
);
233 sd_netlink_message_ref(m
);
235 if (sd_netlink_message_is_broadcast(m
))
238 serial
= message_get_serial(m
);
242 if (sd_netlink_message_get_errno(m
) < 0) {
243 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*old
= NULL
;
245 old
= hashmap_remove(nl
->rqueue_by_serial
, UINT32_TO_PTR(serial
));
247 log_debug("sd-netlink: received error message with serial %"PRIu32
", but another message with "
248 "the same serial is already stored in the read queue, replacing.", serial
);
251 r
= hashmap_ensure_put(&nl
->rqueue_by_serial
, &netlink_message_hash_ops
, UINT32_TO_PTR(serial
), m
);
253 if (!sd_netlink_message_is_error(m
))
254 log_debug("sd-netlink: received message with serial %"PRIu32
", but another message with "
255 "the same serial is already stored in the read queue, ignoring.", serial
);
259 sd_netlink_message_unref(ordered_set_remove(nl
->rqueue
, m
));
263 sd_netlink_message_ref(m
);
267 static int netlink_queue_partially_received_message(sd_netlink
*nl
, sd_netlink_message
*m
) {
273 assert(m
->hdr
->nlmsg_flags
& NLM_F_MULTI
);
275 if (hashmap_size(nl
->rqueue_partial_by_serial
) >= NETLINK_RQUEUE_MAX
)
276 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS
),
277 "sd-netlink: exhausted the partial read queue size (%d)", NETLINK_RQUEUE_MAX
);
279 serial
= message_get_serial(m
);
280 r
= hashmap_ensure_put(&nl
->rqueue_partial_by_serial
, &netlink_message_hash_ops
, UINT32_TO_PTR(serial
), m
);
284 sd_netlink_message_ref(m
);
288 static int parse_message_one(sd_netlink
*nl
, uint32_t group
, const struct nlmsghdr
*hdr
, sd_netlink_message
**ret
) {
289 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
297 /* not broadcast and not for us */
298 if (group
== 0 && hdr
->nlmsg_pid
!= nl
->sockaddr
.nl
.nl_pid
)
301 /* silently drop noop messages */
302 if (hdr
->nlmsg_type
== NLMSG_NOOP
)
305 /* check that we support this message type */
306 r
= netlink_get_policy_set_and_header_size(nl
, hdr
->nlmsg_type
, hdr
->nlmsg_flags
, NULL
, &size
);
307 if (r
== -EOPNOTSUPP
) {
308 log_debug("sd-netlink: ignored message with unknown type: %i", hdr
->nlmsg_type
);
314 /* check that the size matches the message type */
315 if (hdr
->nlmsg_len
< NLMSG_LENGTH(size
)) {
316 log_debug("sd-netlink: message is shorter than expected, dropping.");
320 r
= message_new_empty(nl
, &m
);
324 m
->multicast_group
= group
;
325 m
->hdr
= memdup(hdr
, hdr
->nlmsg_len
);
329 /* seal and parse the top-level message */
330 r
= sd_netlink_message_rewind(m
, nl
);
342 /* On success, the number of bytes received is returned and *ret points to the received message
343 * which has a valid header and the correct size.
344 * If nothing useful was received 0 is returned.
345 * On failure, a negative error code is returned.
347 int socket_read_message(sd_netlink
*nl
) {
355 /* read nothing, just get the pending message size */
356 r
= socket_recv_message(nl
->fd
, NULL
, 0, NULL
, true);
361 /* make room for the pending message */
362 if (!greedy_realloc((void**) &nl
->rbuffer
, len
, sizeof(uint8_t)))
365 /* read the pending message */
366 r
= socket_recv_message(nl
->fd
, nl
->rbuffer
, MALLOC_SIZEOF_SAFE(nl
->rbuffer
), &group
, false);
371 if (!NLMSG_OK(nl
->rbuffer
, len
)) {
372 log_debug("sd-netlink: received invalid message, discarding %zu bytes of incoming message", len
);
376 for (struct nlmsghdr
*hdr
= nl
->rbuffer
; NLMSG_OK(hdr
, len
); hdr
= NLMSG_NEXT(hdr
, len
)) {
377 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
379 r
= parse_message_one(nl
, group
, hdr
, &m
);
385 if (hdr
->nlmsg_flags
& NLM_F_MULTI
) {
386 if (hdr
->nlmsg_type
== NLMSG_DONE
) {
387 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*existing
= NULL
;
389 /* finished reading multi-part message */
390 existing
= hashmap_remove(nl
->rqueue_partial_by_serial
, UINT32_TO_PTR(hdr
->nlmsg_seq
));
392 /* if we receive only NLMSG_DONE, put it into the receive queue. */
393 r
= netlink_queue_received_message(nl
, existing
?: m
);
399 sd_netlink_message
*existing
;
401 existing
= hashmap_get(nl
->rqueue_partial_by_serial
, UINT32_TO_PTR(hdr
->nlmsg_seq
));
403 /* This is the continuation of the previously read messages.
404 * Let's append this message at the end. */
405 while (existing
->next
)
406 existing
= existing
->next
;
407 existing
->next
= TAKE_PTR(m
);
409 /* This is the first message. Put it into the queue for partially
410 * received messages. */
411 r
= netlink_queue_partially_received_message(nl
, m
);
418 r
= netlink_queue_received_message(nl
, m
);
427 log_debug("sd-netlink: discarding trailing %zu bytes of incoming message", len
);