]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-rtnl/rtnl-message.c
Update TODO
[thirdparty/systemd.git] / src / libsystemd-rtnl / rtnl-message.c
CommitLineData
65f568bb
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
a33dece5 22#include <linux/rtnetlink.h>
65f568bb
TG
23#include <netinet/in.h>
24#include <netinet/ether.h>
25#include <stdbool.h>
26#include <unistd.h>
27
28#include "util.h"
29#include "refcnt.h"
30
31#include "sd-rtnl.h"
32#include "rtnl-internal.h"
33
34struct sd_rtnl_message {
35 RefCount n_ref;
36
37 struct nlmsghdr *hdr;
38
39 struct rtattr *next_rta;
40 size_t remaining_size;
41
42 bool sealed:1;
43};
44
45static int message_new(sd_rtnl_message **ret, size_t initial_size) {
46 sd_rtnl_message *m;
47
48 assert_return(ret, -EINVAL);
dabfa9d1 49 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
65f568bb
TG
50
51 m = new0(sd_rtnl_message, 1);
52 if (!m)
53 return -ENOMEM;
54
dc7d8997 55 m->hdr = malloc0(initial_size);
65f568bb
TG
56 if (!m->hdr) {
57 free(m);
58 return -ENOMEM;
59 }
60
61 m->n_ref = REFCNT_INIT;
62
63 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
64 m->sealed = false;
65
66 *ret = m;
67
68 return 0;
69}
70
03d7e632
TG
71int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
72 unsigned char rtm_dst_len, unsigned char rtm_src_len,
73 unsigned char rtm_tos, unsigned char rtm_table,
74 unsigned char rtm_scope, unsigned char rtm_protocol,
75 unsigned char rtm_type, unsigned rtm_flags, sd_rtnl_message **ret) {
76 struct rtmsg *rtm;
77 int r;
78
79 assert_return(nlmsg_type == RTM_NEWROUTE || nlmsg_type == RTM_DELROUTE ||
80 nlmsg_type == RTM_GETROUTE, -EINVAL);
81 assert_return(ret, -EINVAL);
82
83 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
84 if (r < 0)
85 return r;
86
87 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
88 (*ret)->hdr->nlmsg_type = nlmsg_type;
89 if (nlmsg_type == RTM_NEWROUTE)
90 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
91
92 rtm = NLMSG_DATA((*ret)->hdr);
93
94 rtm->rtm_family = rtm_family;
95 rtm->rtm_dst_len = rtm_dst_len;
96 rtm->rtm_src_len = rtm_src_len;
97 rtm->rtm_tos = rtm_tos;
98 rtm->rtm_table = rtm_table;
99 rtm->rtm_protocol = rtm_protocol;
100 rtm->rtm_scope = rtm_scope;
101 rtm->rtm_type = rtm_type;
102 rtm->rtm_flags = rtm_flags;
103
104 return 0;
105}
106
dabfa9d1 107int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, unsigned int type, unsigned int flags, sd_rtnl_message **ret) {
65f568bb
TG
108 struct ifinfomsg *ifi;
109 int r;
110
111 assert_return(nlmsg_type == RTM_NEWLINK || nlmsg_type == RTM_DELLINK || nlmsg_type == RTM_GETLINK, -EINVAL);
112 assert_return(index > 0, -EINVAL);
113 assert_return(ret, -EINVAL);
114
115 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
116 if (r < 0)
117 return r;
118
119 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
120 (*ret)->hdr->nlmsg_type = nlmsg_type;
121
dabfa9d1 122 ifi = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
123
124 ifi->ifi_family = AF_UNSPEC;
125 ifi->ifi_index = index;
126 ifi->ifi_type = type;
127 ifi->ifi_flags = flags;
128 ifi->ifi_change = 0xffffffff;
129
130 return 0;
131}
132
dabfa9d1 133int sd_rtnl_message_addr_new(uint16_t nlmsg_type, int index, unsigned char family, unsigned char prefixlen, unsigned char flags, unsigned char scope, sd_rtnl_message **ret) {
65f568bb
TG
134 struct ifaddrmsg *ifa;
135 int r;
136
137 assert_return(nlmsg_type == RTM_NEWADDR || nlmsg_type == RTM_DELADDR || nlmsg_type == RTM_GETADDR, -EINVAL);
138 assert_return(index > 0, -EINVAL);
139 assert_return(ret, -EINVAL);
140
141 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
142 if (r < 0)
143 return r;
144
145 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
146 (*ret)->hdr->nlmsg_type = nlmsg_type;
147
dabfa9d1 148 ifa = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
149
150 ifa->ifa_family = family;
151 ifa->ifa_prefixlen = prefixlen;
152 ifa->ifa_flags = flags;
153 ifa->ifa_scope = scope;
154 ifa->ifa_index = index;
155
156 return 0;
157}
158
159sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
160 if (m)
161 assert_se(REFCNT_INC(m->n_ref) >= 2);
162
163 return m;
164}
165
166sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
167 if (m && REFCNT_DEC(m->n_ref) <= 0) {
168 free(m->hdr);
169 free(m);
170 }
171
172 return NULL;
173}
174
dabfa9d1 175int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
65f568bb
TG
176 assert_return(m, -EINVAL);
177 assert_return(type, -EINVAL);
178
179 *type = m->hdr->nlmsg_type;
180
181 return 0;
182}
183
184/* If successful the updated message will be correctly aligned, if unsuccessful the old message is
185 untouched */
186static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
dabfa9d1 187 uint32_t rta_length, message_length;
65f568bb
TG
188 struct nlmsghdr *new_hdr;
189 struct rtattr *rta;
8e337e64 190 char *padding;
65f568bb
TG
191
192 assert_return(m, -EINVAL);
193 assert_return(m->hdr, -EINVAL);
194 assert_return(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len, -EINVAL);
195 assert_return(data, -EINVAL);
196 assert_return(data_length > 0, -EINVAL);
197
8e337e64 198 /* get the size of the new rta attribute (with padding at the end) */
65f568bb 199 rta_length = RTA_LENGTH(data_length);
8e337e64 200 /* get the new message size (with padding at the end)
65f568bb
TG
201 */
202 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
203
204 /* realloc to fit the new attribute */
205 new_hdr = realloc(m->hdr, message_length);
206 if (!new_hdr)
207 return -ENOMEM;
208 m->hdr = new_hdr;
209
210 /* get pointer to the attribute we are about to add */
211 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
212 /* update message size */
213 m->hdr->nlmsg_len = message_length;
214
215 /* fill in the attribute */
216 rta->rta_type = type;
217 rta->rta_len = rta_length;
218 /* we don't deal with the case where the user lies about the type and gives us
219 * too little data (so don't do that)
220 */
8e337e64
TG
221 padding = mempcpy(RTA_DATA(rta), data, data_length);
222 /* make sure also the padding at the end of the message is initialized */
223 memset(padding, '\0', (unsigned char *) m->hdr + m->hdr->nlmsg_len - (unsigned char *) padding);
65f568bb
TG
224
225 return 0;
226}
227
228int sd_rtnl_message_append(sd_rtnl_message *m, unsigned short type, const void *data) {
dabfa9d1 229 uint16_t rtm_type;
65f568bb 230 struct ifaddrmsg *ifa;
03d7e632 231 struct rtmsg *rtm;
65f568bb
TG
232
233 assert_return(m, -EINVAL);
234 assert_return(data, -EINVAL);
235
236 sd_rtnl_message_get_type(m, &rtm_type);
237
238 switch (rtm_type) {
239 case RTM_NEWLINK:
240 case RTM_DELLINK:
241 case RTM_GETLINK:
242 switch (type) {
243 case IFLA_IFNAME:
244 case IFLA_QDISC:
245 return add_rtattr(m, type, data, strlen(data) + 1);
246 case IFLA_MTU:
03d7e632 247 return add_rtattr(m, type, data, sizeof(uint32_t));
65f568bb 248 case IFLA_LINK:
03d7e632 249 return add_rtattr(m, type, data, sizeof(uint32_t));
65f568bb
TG
250 case IFLA_STATS:
251 return add_rtattr(m, type, data, sizeof(struct rtnl_link_stats));
252 case IFLA_ADDRESS:
253 case IFLA_BROADCAST:
254 return add_rtattr(m, type, data, ETH_ALEN);
255 default:
256 return -ENOTSUP;
257 }
258 case RTM_NEWADDR:
259 case RTM_DELADDR:
260 case RTM_GETADDR:
261 switch (type) {
262 case IFA_LABEL:
263 return add_rtattr(m, type, data, strlen(data) + 1);
264 case IFA_ADDRESS:
265 case IFA_LOCAL:
266 case IFA_BROADCAST:
267 case IFA_ANYCAST:
268 ifa = NLMSG_DATA(m->hdr);
269 switch (ifa->ifa_family) {
270 case AF_INET:
271 return add_rtattr(m, type, data, sizeof(struct in_addr));
272 case AF_INET6:
273 return add_rtattr(m, type, data, sizeof(struct in6_addr));
274 default:
275 return -EINVAL;
276 }
277 default:
278 return -ENOTSUP;
279 }
03d7e632
TG
280 case RTM_NEWROUTE:
281 case RTM_DELROUTE:
282 case RTM_GETROUTE:
283 switch (type) {
284 case RTA_DST:
285 case RTA_SRC:
286 case RTA_GATEWAY:
287 rtm = NLMSG_DATA(m->hdr);
288 switch (rtm->rtm_family) {
289 case AF_INET:
290 return add_rtattr(m, type, data, sizeof(struct in_addr));
291 case AF_INET6:
292 return add_rtattr(m, type, data, sizeof(struct in6_addr));
293 default:
294 return -EINVAL;
295 }
296 case RTA_TABLE:
297 case RTA_PRIORITY:
298 case RTA_IIF:
299 case RTA_OIF:
300 return add_rtattr(m, type, data, sizeof(uint32_t));
301 default:
302 return -ENOTSUP;
303 }
65f568bb
TG
304 default:
305 return -ENOTSUP;
306 }
307}
308
309static int message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
310 assert_return(m, -EINVAL);
311 assert_return(data, -EINVAL);
312
313 if (!RTA_OK(m->next_rta, m->remaining_size))
314 return 0;
315
316 *data = RTA_DATA(m->next_rta);
317 *type = m->next_rta->rta_type;
318
319 m->next_rta = RTA_NEXT(m->next_rta, m->remaining_size);
320
321 return 1;
322}
323
324int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
dabfa9d1 325 uint16_t rtm_type;
65f568bb
TG
326
327 assert_return(m, -EINVAL);
328 assert_return(data, -EINVAL);
329
330 sd_rtnl_message_get_type(m, &rtm_type);
331
332 switch (rtm_type) {
333 case RTM_NEWLINK:
334 case RTM_DELLINK:
335 case RTM_GETLINK:
336 if (!m->next_rta) {
dabfa9d1 337 struct ifinfomsg *ifi = NLMSG_DATA(m->hdr);
65f568bb
TG
338
339 m->next_rta = IFLA_RTA(ifi);
340 m->remaining_size = IFLA_PAYLOAD(m->hdr);
341 }
03d7e632 342 break;
65f568bb
TG
343 case RTM_NEWADDR:
344 case RTM_DELADDR:
345 case RTM_GETADDR:
346 if (!m->next_rta) {
dabfa9d1 347 struct ifaddrmsg *ifa = NLMSG_DATA(m->hdr);
65f568bb
TG
348
349 m->next_rta = IFA_RTA(ifa);
03d7e632
TG
350 m->remaining_size = IFA_PAYLOAD(m->hdr);
351 }
352 break;
353 case RTM_NEWROUTE:
354 case RTM_DELROUTE:
355 case RTM_GETROUTE:
356 if (!m->next_rta) {
357 struct rtmesg *rtm = NLMSG_DATA(m->hdr);
358
359 m->next_rta = RTM_RTA(rtm);
360 m->remaining_size = RTM_PAYLOAD(m->hdr);
65f568bb 361 }
03d7e632 362 break;
65f568bb
TG
363 default:
364 return -ENOTSUP;
365 }
366
367 return message_read(m, type, data);
368}
369
4555ec72 370uint32_t message_get_serial(sd_rtnl_message *m) {
65f568bb
TG
371 assert(m);
372
373 return m->hdr->nlmsg_seq;
374}
375
376int message_get_errno(sd_rtnl_message *m) {
377 struct nlmsgerr *err;
378
379 assert(m);
380
381 if (m->hdr->nlmsg_type != NLMSG_ERROR)
382 return 0;
383
384 err = NLMSG_DATA(m->hdr);
385
386 return err->error;
387}
388
389int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
390 if (m->sealed)
391 return -EPERM;
392
393 m->hdr->nlmsg_seq = nl->serial++;
394 m->sealed = true;
395
396 return 0;
397}
398
399static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
400 assert_return(rtnl, -EINVAL);
401 assert_return(need, -EINVAL);
402
403 /* ioctl(rtnl->fd, FIONREAD, &need)
dabfa9d1
TG
404 Does not appear to work on netlink sockets. libnl uses
405 MSG_PEEK instead. I don't know if that is worth the
406 extra roundtrip.
407
408 For now we simply use the maximum message size the kernel
409 may use (NLMSG_GOODSIZE), and then realloc to the actual
410 size after reading the message (hence avoiding huge memory
411 usage in case many small messages are kept around) */
412 *need = page_size();
65f568bb
TG
413 if (*need > 8192UL)
414 *need = 8192UL;
415
416 return 0;
417}
418
419/* returns the number of bytes sent, or a negative error code */
420int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
d4bbdb77
TG
421 union {
422 struct sockaddr sa;
423 struct sockaddr_nl nl;
424 } addr = {
425 .nl.nl_family = AF_NETLINK,
426 };
65f568bb
TG
427 ssize_t k;
428
429 assert_return(nl, -EINVAL);
430 assert_return(m, -EINVAL);
431
432 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
d4bbdb77 433 0, &addr.sa, sizeof(addr));
65f568bb
TG
434 if (k < 0)
435 return (errno == EAGAIN) ? 0 : -errno;
436
437 return k;
438}
439
440/* On success, the number of bytes received is returned and *ret points to the received message
441 * which has a valid header and the correct size.
442 * If nothing useful was received 0 is returned.
443 * On failure, a negative error code is returned.
dabfa9d1 444 */
65f568bb
TG
445int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
446 sd_rtnl_message *m;
d4bbdb77
TG
447 union {
448 struct sockaddr sa;
449 struct sockaddr_nl nl;
450 } addr;
451 socklen_t addr_len;
65f568bb
TG
452 int r;
453 ssize_t k;
454 size_t need;
455
456 assert_return(nl, -EINVAL);
457 assert_return(ret, -EINVAL);
458
459 r = message_receive_need(nl, &need);
460 if (r < 0)
461 return r;
462
463 r = message_new(&m, need);
464 if (r < 0)
465 return r;
466
d4bbdb77
TG
467 addr_len = sizeof(addr);
468
65f568bb 469 k = recvfrom(nl->fd, m->hdr, need,
d4bbdb77 470 0, &addr.sa, &addr_len);
65f568bb 471 if (k < 0)
276fc066 472 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
65f568bb
TG
473 else if (k == 0)
474 k = -ECONNRESET; /* connection was closed by the kernel */
d4bbdb77
TG
475 else if (addr_len != sizeof(addr.nl) ||
476 addr.nl.nl_family != AF_NETLINK)
65f568bb 477 k = -EIO; /* not a netlink message */
d4bbdb77 478 else if (addr.nl.nl_pid != 0)
65f568bb
TG
479 k = 0; /* not from the kernel */
480 else if ((size_t) k < sizeof(struct nlmsghdr) ||
dabfa9d1 481 (size_t) k < m->hdr->nlmsg_len)
65f568bb 482 k = -EIO; /* too small (we do accept too big though) */
d4bbdb77 483 else if (m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
65f568bb
TG
484 k = 0; /* not for us */
485
486 if (k > 0)
487 switch (m->hdr->nlmsg_type) {
488 /* check that the size matches the message type */
489 case NLMSG_ERROR:
490 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
491 k = -EIO;
03d7e632 492 break;
65f568bb
TG
493 case RTM_NEWLINK:
494 case RTM_DELLINK:
495 case RTM_GETLINK:
496 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
497 k = -EIO;
03d7e632 498 break;
65f568bb
TG
499 case RTM_NEWADDR:
500 case RTM_DELADDR:
501 case RTM_GETADDR:
502 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
503 k = -EIO;
03d7e632 504 break;
65f568bb
TG
505 case NLMSG_NOOP:
506 k = 0;
03d7e632 507 break;
65f568bb
TG
508 default:
509 k = 0; /* ignoring message of unknown type */
510 }
511
512 if (k <= 0)
513 sd_rtnl_message_unref(m);
514 else {
515 /* we probably allocated way too much memory, give it back */
516 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
517 *ret = m;
518 }
519
520 return k;
521}