Line data Source code
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 "util.h"
27 : #include "socket-util.h"
28 : #include "formats-util.h"
29 : #include "refcnt.h"
30 : #include "missing.h"
31 :
32 : #include "sd-netlink.h"
33 : #include "netlink-util.h"
34 : #include "netlink-internal.h"
35 : #include "netlink-types.h"
36 :
37 10 : int socket_open(int family) {
38 : int fd;
39 :
40 10 : fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
41 10 : if (fd < 0)
42 0 : return -errno;
43 :
44 10 : return fd;
45 : }
46 :
47 10 : int socket_bind(sd_netlink *nl) {
48 : socklen_t addrlen;
49 10 : int r, one = 1;
50 :
51 10 : r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
52 10 : if (r < 0)
53 0 : return -errno;
54 :
55 10 : addrlen = sizeof(nl->sockaddr);
56 :
57 10 : r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
58 : /* ignore EINVAL to allow opening an already bound socket */
59 10 : if (r < 0 && errno != EINVAL)
60 0 : return -errno;
61 :
62 10 : r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
63 10 : if (r < 0)
64 0 : return -errno;
65 :
66 10 : return 0;
67 : }
68 :
69 :
70 8 : int socket_join_broadcast_group(sd_netlink *nl, unsigned group) {
71 : int r;
72 :
73 8 : assert(nl);
74 8 : assert(nl->fd >= 0);
75 8 : assert(group > 0);
76 :
77 8 : r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
78 8 : if (r < 0)
79 0 : return -errno;
80 :
81 8 : return 0;
82 : }
83 :
84 : /* returns the number of bytes sent, or a negative error code */
85 16 : int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
86 : union {
87 : struct sockaddr sa;
88 : struct sockaddr_nl nl;
89 16 : } addr = {
90 : .nl.nl_family = AF_NETLINK,
91 : };
92 : ssize_t k;
93 :
94 16 : assert(nl);
95 16 : assert(m);
96 16 : assert(m->hdr);
97 :
98 16 : k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
99 : 0, &addr.sa, sizeof(addr));
100 16 : if (k < 0)
101 0 : return -errno;
102 :
103 16 : return k;
104 : }
105 :
106 50 : 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 50 : struct msghdr msg = {
110 : .msg_iov = iov,
111 : .msg_iovlen = 1,
112 : .msg_name = &sender,
113 : .msg_namelen = sizeof(sender),
114 : .msg_control = cmsg_buffer,
115 : .msg_controllen = sizeof(cmsg_buffer),
116 : };
117 : struct cmsghdr *cmsg;
118 50 : uint32_t group = 0;
119 : int r;
120 :
121 50 : assert(fd >= 0);
122 50 : assert(iov);
123 :
124 50 : r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
125 50 : if (r < 0) {
126 : /* no data */
127 0 : if (errno == ENOBUFS)
128 0 : log_debug("rtnl: kernel receive buffer overrun");
129 0 : else if (errno == EAGAIN)
130 0 : log_debug("rtnl: no data in socket");
131 :
132 0 : return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
133 : }
134 :
135 50 : if (sender.nl.nl_pid != 0) {
136 : /* not from the kernel, ignore */
137 0 : log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
138 :
139 0 : if (peek) {
140 : /* drop the message */
141 0 : r = recvmsg(fd, &msg, 0);
142 0 : if (r < 0)
143 0 : return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
144 : }
145 :
146 0 : return 0;
147 : }
148 :
149 100 : CMSG_FOREACH(cmsg, &msg) {
150 100 : if (cmsg->cmsg_level == SOL_NETLINK &&
151 100 : cmsg->cmsg_type == NETLINK_PKTINFO &&
152 50 : cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
153 50 : struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
154 :
155 : /* multi-cast group */
156 50 : group = pktinfo->group;
157 : }
158 : }
159 :
160 50 : if (_group)
161 25 : *_group = group;
162 :
163 50 : return r;
164 : }
165 :
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.
170 : */
171 25 : int socket_read_message(sd_netlink *rtnl) {
172 50 : _cleanup_netlink_message_unref_ sd_netlink_message *first = NULL;
173 25 : struct iovec iov = {};
174 25 : uint32_t group = 0;
175 25 : bool multi_part = false, done = false;
176 : struct nlmsghdr *new_msg;
177 : size_t len;
178 : int r;
179 25 : unsigned i = 0;
180 :
181 25 : assert(rtnl);
182 25 : assert(rtnl->rbuffer);
183 25 : assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
184 :
185 : /* read nothing, just get the pending message size */
186 25 : r = socket_recv_message(rtnl->fd, &iov, NULL, true);
187 25 : if (r <= 0)
188 0 : return r;
189 : else
190 25 : len = (size_t)r;
191 :
192 : /* make room for the pending message */
193 25 : if (!greedy_realloc((void **)&rtnl->rbuffer,
194 : &rtnl->rbuffer_allocated,
195 : len, sizeof(uint8_t)))
196 0 : return -ENOMEM;
197 :
198 25 : iov.iov_base = rtnl->rbuffer;
199 25 : iov.iov_len = rtnl->rbuffer_allocated;
200 :
201 : /* read the pending message */
202 25 : r = socket_recv_message(rtnl->fd, &iov, &group, false);
203 25 : if (r <= 0)
204 0 : return r;
205 : else
206 25 : len = (size_t)r;
207 :
208 25 : if (len > rtnl->rbuffer_allocated)
209 : /* message did not fit in read buffer */
210 0 : return -EIO;
211 :
212 25 : if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
213 14 : multi_part = true;
214 :
215 14 : for (i = 0; i < rtnl->rqueue_partial_size; i++) {
216 18 : if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
217 9 : rtnl->rbuffer->nlmsg_seq) {
218 9 : first = rtnl->rqueue_partial[i];
219 9 : break;
220 : }
221 : }
222 : }
223 :
224 82 : for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
225 114 : _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
226 : const NLType *nl_type;
227 :
228 57 : if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
229 : /* not broadcast and not for us */
230 0 : continue;
231 :
232 57 : if (new_msg->nlmsg_type == NLMSG_NOOP)
233 : /* silently drop noop messages */
234 0 : continue;
235 :
236 57 : if (new_msg->nlmsg_type == NLMSG_DONE) {
237 : /* finished reading multi-part message */
238 5 : done = true;
239 :
240 : /* if first is not defined, put NLMSG_DONE into the receive queue. */
241 5 : if (first)
242 5 : continue;
243 : }
244 :
245 : /* check that we support this message type */
246 52 : r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type);
247 52 : if (r < 0) {
248 0 : if (r == -EOPNOTSUPP)
249 0 : log_debug("sd-netlink: ignored message with unknown type: %i",
250 : new_msg->nlmsg_type);
251 :
252 0 : continue;
253 : }
254 :
255 : /* check that the size matches the message type */
256 52 : if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
257 0 : log_debug("sd-netlink: message larger than expected, dropping");
258 0 : continue;
259 : }
260 :
261 52 : r = message_new_empty(rtnl, &m);
262 52 : if (r < 0)
263 0 : return r;
264 :
265 52 : m->broadcast = !!group;
266 :
267 52 : m->hdr = memdup(new_msg, new_msg->nlmsg_len);
268 52 : if (!m->hdr)
269 0 : return -ENOMEM;
270 :
271 : /* seal and parse the top-level message */
272 52 : r = sd_netlink_message_rewind(m);
273 52 : if (r < 0)
274 0 : return r;
275 :
276 : /* push the message onto the multi-part message stack */
277 52 : if (first)
278 36 : m->next = first;
279 52 : first = m;
280 52 : m = NULL;
281 : }
282 :
283 25 : if (len)
284 0 : log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
285 :
286 25 : if (!first)
287 0 : return 0;
288 :
289 25 : if (!multi_part || done) {
290 : /* we got a complete message, push it on the read queue */
291 16 : r = rtnl_rqueue_make_room(rtnl);
292 16 : if (r < 0)
293 0 : return r;
294 :
295 16 : rtnl->rqueue[rtnl->rqueue_size ++] = first;
296 16 : first = NULL;
297 :
298 16 : if (multi_part && (i < rtnl->rqueue_partial_size)) {
299 : /* remove the message form the partial read queue */
300 5 : memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
301 5 : sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
302 5 : rtnl->rqueue_partial_size --;
303 : }
304 :
305 16 : return 1;
306 : } else {
307 : /* we only got a partial multi-part message, push it on the
308 : partial read queue */
309 9 : if (i < rtnl->rqueue_partial_size) {
310 4 : rtnl->rqueue_partial[i] = first;
311 : } else {
312 5 : r = rtnl_rqueue_partial_make_room(rtnl);
313 5 : if (r < 0)
314 0 : return r;
315 :
316 5 : rtnl->rqueue_partial[rtnl->rqueue_partial_size ++] = first;
317 : }
318 9 : first = NULL;
319 :
320 9 : return 0;
321 : }
322 : }
|