]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-netlink/netlink-socket.c
Merge pull request #205 from endocode/iaguis/seccomp-v2
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-socket.c
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 int socket_open(int family) {
38 int fd;
39
40 fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
41 if (fd < 0)
42 return -errno;
43
44 return fd;
45 }
46
47 int socket_bind(sd_netlink *nl) {
48 socklen_t addrlen;
49 int r, one = 1;
50
51 r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
52 if (r < 0)
53 return -errno;
54
55 addrlen = sizeof(nl->sockaddr);
56
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)
60 return -errno;
61
62 r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
63 if (r < 0)
64 return -errno;
65
66 return 0;
67 }
68
69
70 int socket_join_broadcast_group(sd_netlink *nl, unsigned group) {
71 int r;
72
73 assert(nl);
74 assert(nl->fd >= 0);
75 assert(group > 0);
76
77 r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
78 if (r < 0)
79 return -errno;
80
81 return 0;
82 }
83
84 /* returns the number of bytes sent, or a negative error code */
85 int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
86 union {
87 struct sockaddr sa;
88 struct sockaddr_nl nl;
89 } addr = {
90 .nl.nl_family = AF_NETLINK,
91 };
92 ssize_t k;
93
94 assert(nl);
95 assert(m);
96 assert(m->hdr);
97
98 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
99 0, &addr.sa, sizeof(addr));
100 if (k < 0)
101 return -errno;
102
103 return k;
104 }
105
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 = {
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 uint32_t group = 0;
119 int r;
120
121 assert(fd >= 0);
122 assert(iov);
123
124 r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
125 if (r < 0) {
126 /* no data */
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");
131
132 return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
133 }
134
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);
138
139 if (peek) {
140 /* drop the message */
141 r = recvmsg(fd, &msg, 0);
142 if (r < 0)
143 return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
144 }
145
146 return 0;
147 }
148
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);
154
155 /* multi-cast group */
156 group = pktinfo->group;
157 }
158 }
159
160 if (_group)
161 *_group = group;
162
163 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 int socket_read_message(sd_netlink *rtnl) {
172 _cleanup_netlink_message_unref_ sd_netlink_message *first = NULL;
173 struct iovec iov = {};
174 uint32_t group = 0;
175 bool multi_part = false, done = false;
176 struct nlmsghdr *new_msg;
177 size_t len;
178 int r;
179 unsigned i = 0;
180
181 assert(rtnl);
182 assert(rtnl->rbuffer);
183 assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
184
185 /* read nothing, just get the pending message size */
186 r = socket_recv_message(rtnl->fd, &iov, NULL, true);
187 if (r <= 0)
188 return r;
189 else
190 len = (size_t)r;
191
192 /* make room for the pending message */
193 if (!greedy_realloc((void **)&rtnl->rbuffer,
194 &rtnl->rbuffer_allocated,
195 len, sizeof(uint8_t)))
196 return -ENOMEM;
197
198 iov.iov_base = rtnl->rbuffer;
199 iov.iov_len = rtnl->rbuffer_allocated;
200
201 /* read the pending message */
202 r = socket_recv_message(rtnl->fd, &iov, &group, false);
203 if (r <= 0)
204 return r;
205 else
206 len = (size_t)r;
207
208 if (len > rtnl->rbuffer_allocated)
209 /* message did not fit in read buffer */
210 return -EIO;
211
212 if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
213 multi_part = true;
214
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];
219 break;
220 }
221 }
222 }
223
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;
227
228 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
229 /* not broadcast and not for us */
230 continue;
231
232 if (new_msg->nlmsg_type == NLMSG_NOOP)
233 /* silently drop noop messages */
234 continue;
235
236 if (new_msg->nlmsg_type == NLMSG_DONE) {
237 /* finished reading multi-part message */
238 done = true;
239
240 /* if first is not defined, put NLMSG_DONE into the receive queue. */
241 if (first)
242 continue;
243 }
244
245 /* check that we support this message type */
246 r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type);
247 if (r < 0) {
248 if (r == -EOPNOTSUPP)
249 log_debug("sd-netlink: ignored message with unknown type: %i",
250 new_msg->nlmsg_type);
251
252 continue;
253 }
254
255 /* check that the size matches the message type */
256 if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) {
257 log_debug("sd-netlink: message larger than expected, dropping");
258 continue;
259 }
260
261 r = message_new_empty(rtnl, &m);
262 if (r < 0)
263 return r;
264
265 m->broadcast = !!group;
266
267 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
268 if (!m->hdr)
269 return -ENOMEM;
270
271 /* seal and parse the top-level message */
272 r = sd_netlink_message_rewind(m);
273 if (r < 0)
274 return r;
275
276 /* push the message onto the multi-part message stack */
277 if (first)
278 m->next = first;
279 first = m;
280 m = NULL;
281 }
282
283 if (len)
284 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
285
286 if (!first)
287 return 0;
288
289 if (!multi_part || done) {
290 /* we got a complete message, push it on the read queue */
291 r = rtnl_rqueue_make_room(rtnl);
292 if (r < 0)
293 return r;
294
295 rtnl->rqueue[rtnl->rqueue_size ++] = first;
296 first = NULL;
297
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 --;
303 }
304
305 return 1;
306 } else {
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;
311 } else {
312 r = rtnl_rqueue_partial_make_room(rtnl);
313 if (r < 0)
314 return r;
315
316 rtnl->rqueue_partial[rtnl->rqueue_partial_size ++] = first;
317 }
318 first = NULL;
319
320 return 0;
321 }
322 }