2 This file is part of systemd.
4 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
20 #include <netinet/in.h>
24 #include "sd-netlink.h"
26 #include "alloc-util.h"
27 #include "formats-util.h"
29 #include "netlink-internal.h"
30 #include "netlink-types.h"
31 #include "netlink-util.h"
33 #include "socket-util.h"
36 int socket_open(int family
) {
39 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, family
);
46 static int broadcast_groups_get(sd_netlink
*nl
) {
47 _cleanup_free_
uint32_t *groups
= NULL
;
48 socklen_t len
= 0, old_len
;
55 r
= getsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_LIST_MEMBERSHIPS
, NULL
, &len
);
57 if (errno
== ENOPROTOOPT
) {
58 nl
->broadcast_group_dont_leave
= true;
67 groups
= new0(uint32_t, len
);
73 r
= getsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_LIST_MEMBERSHIPS
, groups
, &len
);
80 r
= hashmap_ensure_allocated(&nl
->broadcast_group_refs
, NULL
);
84 for (i
= 0; i
< len
; i
++) {
85 for (j
= 0; j
< sizeof(uint32_t) * 8; j
++) {
91 if (!(groups
[i
] & offset
))
94 group
= i
* sizeof(uint32_t) * 8 + j
+ 1;
96 r
= hashmap_put(nl
->broadcast_group_refs
, UINT_TO_PTR(group
), UINT_TO_PTR(1));
105 int socket_bind(sd_netlink
*nl
) {
109 r
= setsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_PKTINFO
, &one
, sizeof(one
));
113 addrlen
= sizeof(nl
->sockaddr
);
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
)
120 r
= getsockname(nl
->fd
, &nl
->sockaddr
.sa
, &addrlen
);
124 r
= broadcast_groups_get(nl
);
131 static unsigned broadcast_group_get_ref(sd_netlink
*nl
, unsigned group
) {
134 return PTR_TO_UINT(hashmap_get(nl
->broadcast_group_refs
, UINT_TO_PTR(group
)));
137 static int broadcast_group_set_ref(sd_netlink
*nl
, unsigned group
, unsigned n_ref
) {
142 r
= hashmap_replace(nl
->broadcast_group_refs
, UINT_TO_PTR(group
), UINT_TO_PTR(n_ref
));
149 static int broadcast_group_join(sd_netlink
*nl
, unsigned group
) {
156 r
= setsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_ADD_MEMBERSHIP
, &group
, sizeof(group
));
163 int socket_broadcast_group_ref(sd_netlink
*nl
, unsigned group
) {
169 n_ref
= broadcast_group_get_ref(nl
, group
);
173 r
= hashmap_ensure_allocated(&nl
->broadcast_group_refs
, NULL
);
177 r
= broadcast_group_set_ref(nl
, group
, n_ref
);
182 /* not yet in the group */
185 r
= broadcast_group_join(nl
, group
);
192 static int broadcast_group_leave(sd_netlink
*nl
, unsigned group
) {
199 if (nl
->broadcast_group_dont_leave
)
202 r
= setsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_DROP_MEMBERSHIP
, &group
, sizeof(group
));
209 int socket_broadcast_group_unref(sd_netlink
*nl
, unsigned group
) {
215 n_ref
= broadcast_group_get_ref(nl
, group
);
221 r
= broadcast_group_set_ref(nl
, group
, n_ref
);
226 /* still refs left */
229 r
= broadcast_group_leave(nl
, group
);
236 /* returns the number of bytes sent, or a negative error code */
237 int socket_write_message(sd_netlink
*nl
, sd_netlink_message
*m
) {
240 struct sockaddr_nl nl
;
242 .nl
.nl_family
= AF_NETLINK
,
250 k
= sendto(nl
->fd
, m
->hdr
, m
->hdr
->nlmsg_len
,
251 0, &addr
.sa
, sizeof(addr
));
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
= {
265 .msg_namelen
= sizeof(sender
),
266 .msg_control
= cmsg_buffer
,
267 .msg_controllen
= sizeof(cmsg_buffer
),
269 struct cmsghdr
*cmsg
;
276 r
= recvmsg(fd
, &msg
, MSG_TRUNC
| (peek
? MSG_PEEK
: 0));
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");
284 return (errno
== EAGAIN
|| errno
== EINTR
) ? 0 : -errno
;
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
);
292 /* drop the message */
293 r
= recvmsg(fd
, &msg
, 0);
295 return (errno
== EAGAIN
|| errno
== EINTR
) ? 0 : -errno
;
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
);
307 /* multi-cast group */
308 group
= pktinfo
->group
;
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.
323 int socket_read_message(sd_netlink
*rtnl
) {
324 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*first
= NULL
;
325 struct iovec iov
= {};
327 bool multi_part
= false, done
= false;
328 struct nlmsghdr
*new_msg
;
334 assert(rtnl
->rbuffer
);
335 assert(rtnl
->rbuffer_allocated
>= sizeof(struct nlmsghdr
));
337 /* read nothing, just get the pending message size */
338 r
= socket_recv_message(rtnl
->fd
, &iov
, NULL
, true);
344 /* make room for the pending message */
345 if (!greedy_realloc((void **)&rtnl
->rbuffer
,
346 &rtnl
->rbuffer_allocated
,
347 len
, sizeof(uint8_t)))
350 iov
.iov_base
= rtnl
->rbuffer
;
351 iov
.iov_len
= rtnl
->rbuffer_allocated
;
353 /* read the pending message */
354 r
= socket_recv_message(rtnl
->fd
, &iov
, &group
, false);
360 if (len
> rtnl
->rbuffer_allocated
)
361 /* message did not fit in read buffer */
364 if (NLMSG_OK(rtnl
->rbuffer
, len
) && rtnl
->rbuffer
->nlmsg_flags
& NLM_F_MULTI
) {
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
];
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
;
380 if (!group
&& new_msg
->nlmsg_pid
!= rtnl
->sockaddr
.nl
.nl_pid
)
381 /* not broadcast and not for us */
384 if (new_msg
->nlmsg_type
== NLMSG_NOOP
)
385 /* silently drop noop messages */
388 if (new_msg
->nlmsg_type
== NLMSG_DONE
) {
389 /* finished reading multi-part message */
392 /* if first is not defined, put NLMSG_DONE into the receive queue. */
397 /* check that we support this message type */
398 r
= type_system_get_type(&type_system_root
, &nl_type
, new_msg
->nlmsg_type
);
400 if (r
== -EOPNOTSUPP
)
401 log_debug("sd-netlink: ignored message with unknown type: %i",
402 new_msg
->nlmsg_type
);
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");
413 r
= message_new_empty(rtnl
, &m
);
417 m
->broadcast
= !!group
;
419 m
->hdr
= memdup(new_msg
, new_msg
->nlmsg_len
);
423 /* seal and parse the top-level message */
424 r
= sd_netlink_message_rewind(m
);
428 /* push the message onto the multi-part message stack */
436 log_debug("sd-netlink: discarding %zu bytes of incoming message", len
);
441 if (!multi_part
|| done
) {
442 /* we got a complete message, push it on the read queue */
443 r
= rtnl_rqueue_make_room(rtnl
);
447 rtnl
->rqueue
[rtnl
->rqueue_size
++] = first
;
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
--;
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
;
464 r
= rtnl_rqueue_partial_make_room(rtnl
);
468 rtnl
->rqueue_partial
[rtnl
->rqueue_partial_size
++] = first
;