1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
22 #include <netinet/in.h>
27 #include "socket-util.h"
28 #include "formats-util.h"
32 #include "sd-netlink.h"
33 #include "netlink-util.h"
34 #include "netlink-internal.h"
35 #include "netlink-types.h"
37 int socket_open(int family
) {
40 fd
= socket(PF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, family
);
47 int socket_bind(sd_netlink
*nl
) {
51 r
= setsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_PKTINFO
, &one
, sizeof(one
));
55 addrlen
= sizeof(nl
->sockaddr
);
57 r
= bind(nl
->fd
, &nl
->sockaddr
.sa
, addrlen
);
58 /* ignore EINVAL to allow opening an already bound socket */
59 if (r
< 0 && errno
!= EINVAL
)
62 r
= getsockname(nl
->fd
, &nl
->sockaddr
.sa
, &addrlen
);
70 int socket_join_broadcast_group(sd_netlink
*nl
, unsigned group
) {
77 r
= setsockopt(nl
->fd
, SOL_NETLINK
, NETLINK_ADD_MEMBERSHIP
, &group
, sizeof(group
));
84 /* returns the number of bytes sent, or a negative error code */
85 int socket_write_message(sd_netlink
*nl
, sd_netlink_message
*m
) {
88 struct sockaddr_nl nl
;
90 .nl
.nl_family
= AF_NETLINK
,
98 k
= sendto(nl
->fd
, m
->hdr
, m
->hdr
->nlmsg_len
,
99 0, &addr
.sa
, sizeof(addr
));
106 static int socket_recv_message(int fd
, struct iovec
*iov
, uint32_t *_group
, bool peek
) {
107 union sockaddr_union sender
;
108 uint8_t cmsg_buffer
[CMSG_SPACE(sizeof(struct nl_pktinfo
))];
109 struct msghdr msg
= {
113 .msg_namelen
= sizeof(sender
),
114 .msg_control
= cmsg_buffer
,
115 .msg_controllen
= sizeof(cmsg_buffer
),
117 struct cmsghdr
*cmsg
;
124 r
= recvmsg(fd
, &msg
, MSG_TRUNC
| (peek
? MSG_PEEK
: 0));
127 if (errno
== ENOBUFS
)
128 log_debug("rtnl: kernel receive buffer overrun");
129 else if (errno
== EAGAIN
)
130 log_debug("rtnl: no data in socket");
132 return (errno
== EAGAIN
|| errno
== EINTR
) ? 0 : -errno
;
135 if (sender
.nl
.nl_pid
!= 0) {
136 /* not from the kernel, ignore */
137 log_debug("rtnl: ignoring message from portid %"PRIu32
, sender
.nl
.nl_pid
);
140 /* drop the message */
141 r
= recvmsg(fd
, &msg
, 0);
143 return (errno
== EAGAIN
|| errno
== EINTR
) ? 0 : -errno
;
149 CMSG_FOREACH(cmsg
, &msg
) {
150 if (cmsg
->cmsg_level
== SOL_NETLINK
&&
151 cmsg
->cmsg_type
== NETLINK_PKTINFO
&&
152 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct nl_pktinfo
))) {
153 struct nl_pktinfo
*pktinfo
= (void *)CMSG_DATA(cmsg
);
155 /* multi-cast group */
156 group
= pktinfo
->group
;
166 /* On success, the number of bytes received is returned and *ret points to the received message
167 * which has a valid header and the correct size.
168 * If nothing useful was received 0 is returned.
169 * On failure, a negative error code is returned.
171 int socket_read_message(sd_netlink
*rtnl
) {
172 _cleanup_netlink_message_unref_ sd_netlink_message
*first
= NULL
;
173 struct iovec iov
= {};
175 bool multi_part
= false, done
= false;
176 struct nlmsghdr
*new_msg
;
182 assert(rtnl
->rbuffer
);
183 assert(rtnl
->rbuffer_allocated
>= sizeof(struct nlmsghdr
));
185 /* read nothing, just get the pending message size */
186 r
= socket_recv_message(rtnl
->fd
, &iov
, NULL
, true);
192 /* make room for the pending message */
193 if (!greedy_realloc((void **)&rtnl
->rbuffer
,
194 &rtnl
->rbuffer_allocated
,
195 len
, sizeof(uint8_t)))
198 iov
.iov_base
= rtnl
->rbuffer
;
199 iov
.iov_len
= rtnl
->rbuffer_allocated
;
201 /* read the pending message */
202 r
= socket_recv_message(rtnl
->fd
, &iov
, &group
, false);
208 if (len
> rtnl
->rbuffer_allocated
)
209 /* message did not fit in read buffer */
212 if (NLMSG_OK(rtnl
->rbuffer
, len
) && rtnl
->rbuffer
->nlmsg_flags
& NLM_F_MULTI
) {
215 for (i
= 0; i
< rtnl
->rqueue_partial_size
; i
++) {
216 if (rtnl_message_get_serial(rtnl
->rqueue_partial
[i
]) ==
217 rtnl
->rbuffer
->nlmsg_seq
) {
218 first
= rtnl
->rqueue_partial
[i
];
224 for (new_msg
= rtnl
->rbuffer
; NLMSG_OK(new_msg
, len
) && !done
; new_msg
= NLMSG_NEXT(new_msg
, len
)) {
225 _cleanup_netlink_message_unref_ sd_netlink_message
*m
= NULL
;
226 const NLType
*nl_type
;
228 if (!group
&& new_msg
->nlmsg_pid
!= rtnl
->sockaddr
.nl
.nl_pid
)
229 /* not broadcast and not for us */
232 if (new_msg
->nlmsg_type
== NLMSG_NOOP
)
233 /* silently drop noop messages */
236 if (new_msg
->nlmsg_type
== NLMSG_DONE
) {
237 /* finished reading multi-part message */
240 /* if first is not defined, put NLMSG_DONE into the receive queue. */
245 /* check that we support this message type */
246 r
= type_system_get_type(&type_system_root
, &nl_type
, new_msg
->nlmsg_type
);
248 if (r
== -EOPNOTSUPP
)
249 log_debug("sd-netlink: ignored message with unknown type: %i",
250 new_msg
->nlmsg_type
);
255 /* check that the size matches the message type */
256 if (new_msg
->nlmsg_len
< NLMSG_LENGTH(type_get_size(nl_type
))) {
257 log_debug("sd-netlink: message larger than expected, dropping");
261 r
= message_new_empty(rtnl
, &m
);
265 m
->broadcast
= !!group
;
267 m
->hdr
= memdup(new_msg
, new_msg
->nlmsg_len
);
271 /* seal and parse the top-level message */
272 r
= sd_netlink_message_rewind(m
);
276 /* push the message onto the multi-part message stack */
284 log_debug("sd-netlink: discarding %zu bytes of incoming message", len
);
289 if (!multi_part
|| done
) {
290 /* we got a complete message, push it on the read queue */
291 r
= rtnl_rqueue_make_room(rtnl
);
295 rtnl
->rqueue
[rtnl
->rqueue_size
++] = first
;
298 if (multi_part
&& (i
< rtnl
->rqueue_partial_size
)) {
299 /* remove the message form the partial read queue */
300 memmove(rtnl
->rqueue_partial
+ i
,rtnl
->rqueue_partial
+ i
+ 1,
301 sizeof(sd_netlink_message
*) * (rtnl
->rqueue_partial_size
- i
- 1));
302 rtnl
->rqueue_partial_size
--;
307 /* we only got a partial multi-part message, push it on the
308 partial read queue */
309 if (i
< rtnl
->rqueue_partial_size
) {
310 rtnl
->rqueue_partial
[i
] = first
;
312 r
= rtnl_rqueue_partial_make_room(rtnl
);
316 rtnl
->rqueue_partial
[rtnl
->rqueue_partial_size
++] = first
;