]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-socket.c
sd-netlink: message - split up source file
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-socket.c
CommitLineData
89489ef7
TG
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/* returns the number of bytes sent, or a negative error code */
38int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
39 union {
40 struct sockaddr sa;
41 struct sockaddr_nl nl;
42 } addr = {
43 .nl.nl_family = AF_NETLINK,
44 };
45 ssize_t k;
46
47 assert(nl);
48 assert(m);
49 assert(m->hdr);
50
51 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
52 0, &addr.sa, sizeof(addr));
53 if (k < 0)
54 return -errno;
55
56 return k;
57}
58
59static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
60 union sockaddr_union sender;
61 uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
62 struct msghdr msg = {
63 .msg_iov = iov,
64 .msg_iovlen = 1,
65 .msg_name = &sender,
66 .msg_namelen = sizeof(sender),
67 .msg_control = cmsg_buffer,
68 .msg_controllen = sizeof(cmsg_buffer),
69 };
70 struct cmsghdr *cmsg;
71 uint32_t group = 0;
72 int r;
73
74 assert(fd >= 0);
75 assert(iov);
76
77 r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
78 if (r < 0) {
79 /* no data */
80 if (errno == ENOBUFS)
81 log_debug("rtnl: kernel receive buffer overrun");
82 else if (errno == EAGAIN)
83 log_debug("rtnl: no data in socket");
84
85 return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
86 }
87
88 if (sender.nl.nl_pid != 0) {
89 /* not from the kernel, ignore */
90 log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
91
92 if (peek) {
93 /* drop the message */
94 r = recvmsg(fd, &msg, 0);
95 if (r < 0)
96 return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
97 }
98
99 return 0;
100 }
101
102 CMSG_FOREACH(cmsg, &msg) {
103 if (cmsg->cmsg_level == SOL_NETLINK &&
104 cmsg->cmsg_type == NETLINK_PKTINFO &&
105 cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
106 struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
107
108 /* multi-cast group */
109 group = pktinfo->group;
110 }
111 }
112
113 if (_group)
114 *_group = group;
115
116 return r;
117}
118
119/* On success, the number of bytes received is returned and *ret points to the received message
120 * which has a valid header and the correct size.
121 * If nothing useful was received 0 is returned.
122 * On failure, a negative error code is returned.
123 */
124int socket_read_message(sd_netlink *rtnl) {
125 _cleanup_netlink_message_unref_ sd_netlink_message *first = NULL;
126 struct iovec iov = {};
127 uint32_t group = 0;
128 bool multi_part = false, done = false;
129 struct nlmsghdr *new_msg;
130 size_t len;
131 int r;
132 unsigned i = 0;
133
134 assert(rtnl);
135 assert(rtnl->rbuffer);
136 assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
137
138 /* read nothing, just get the pending message size */
139 r = socket_recv_message(rtnl->fd, &iov, NULL, true);
140 if (r <= 0)
141 return r;
142 else
143 len = (size_t)r;
144
145 /* make room for the pending message */
146 if (!greedy_realloc((void **)&rtnl->rbuffer,
147 &rtnl->rbuffer_allocated,
148 len, sizeof(uint8_t)))
149 return -ENOMEM;
150
151 iov.iov_base = rtnl->rbuffer;
152 iov.iov_len = rtnl->rbuffer_allocated;
153
154 /* read the pending message */
155 r = socket_recv_message(rtnl->fd, &iov, &group, false);
156 if (r <= 0)
157 return r;
158 else
159 len = (size_t)r;
160
161 if (len > rtnl->rbuffer_allocated)
162 /* message did not fit in read buffer */
163 return -EIO;
164
165 if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
166 multi_part = true;
167
168 for (i = 0; i < rtnl->rqueue_partial_size; i++) {
169 if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
170 rtnl->rbuffer->nlmsg_seq) {
171 first = rtnl->rqueue_partial[i];
172 break;
173 }
174 }
175 }
176
177 for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
178 _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
179 const NLType *nl_type;
180
181 if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
182 /* not broadcast and not for us */
183 continue;
184
185 if (new_msg->nlmsg_type == NLMSG_NOOP)
186 /* silently drop noop messages */
187 continue;
188
189 if (new_msg->nlmsg_type == NLMSG_DONE) {
190 /* finished reading multi-part message */
191 done = true;
192
193 /* if first is not defined, put NLMSG_DONE into the receive queue. */
194 if (first)
195 continue;
196 }
197
198 /* check that we support this message type */
199 r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type);
200 if (r < 0) {
201 if (r == -EOPNOTSUPP)
202 log_debug("sd-netlink: ignored message with unknown type: %i",
203 new_msg->nlmsg_type);
204
205 continue;
206 }
207
208 /* check that the size matches the message type */
209 if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) {
210 log_debug("sd-netlink: message larger than expected, dropping");
211 continue;
212 }
213
214 r = message_new_empty(rtnl, &m);
215 if (r < 0)
216 return r;
217
218 m->broadcast = !!group;
219
220 m->hdr = memdup(new_msg, new_msg->nlmsg_len);
221 if (!m->hdr)
222 return -ENOMEM;
223
224 /* seal and parse the top-level message */
225 r = sd_netlink_message_rewind(m);
226 if (r < 0)
227 return r;
228
229 /* push the message onto the multi-part message stack */
230 if (first)
231 m->next = first;
232 first = m;
233 m = NULL;
234 }
235
236 if (len)
237 log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
238
239 if (!first)
240 return 0;
241
242 if (!multi_part || done) {
243 /* we got a complete message, push it on the read queue */
244 r = rtnl_rqueue_make_room(rtnl);
245 if (r < 0)
246 return r;
247
248 rtnl->rqueue[rtnl->rqueue_size ++] = first;
249 first = NULL;
250
251 if (multi_part && (i < rtnl->rqueue_partial_size)) {
252 /* remove the message form the partial read queue */
253 memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
254 sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
255 rtnl->rqueue_partial_size --;
256 }
257
258 return 1;
259 } else {
260 /* we only got a partial multi-part message, push it on the
261 partial read queue */
262 if (i < rtnl->rqueue_partial_size) {
263 rtnl->rqueue_partial[i] = first;
264 } else {
265 r = rtnl_rqueue_partial_make_room(rtnl);
266 if (r < 0)
267 return r;
268
269 rtnl->rqueue_partial[rtnl->rqueue_partial_size ++] = first;
270 }
271 first = NULL;
272
273 return 0;
274 }
275}