]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-rtnl/rtnl-message.c
bus: break reference cycle between bus and messages
[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
33125ac5
TG
39 struct rtattr *current_container;
40
65f568bb 41 struct rtattr *next_rta;
65f568bb
TG
42
43 bool sealed:1;
44};
45
46static int message_new(sd_rtnl_message **ret, size_t initial_size) {
47 sd_rtnl_message *m;
48
49 assert_return(ret, -EINVAL);
dabfa9d1 50 assert_return(initial_size >= sizeof(struct nlmsghdr), -EINVAL);
65f568bb
TG
51
52 m = new0(sd_rtnl_message, 1);
53 if (!m)
54 return -ENOMEM;
55
dc7d8997 56 m->hdr = malloc0(initial_size);
65f568bb
TG
57 if (!m->hdr) {
58 free(m);
59 return -ENOMEM;
60 }
61
62 m->n_ref = REFCNT_INIT;
63
64 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
65 m->sealed = false;
66
67 *ret = m;
68
69 return 0;
70}
71
e16bcf98
TG
72int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
73 struct nlmsgerr *err;
74 int r;
75
76 assert(error <= 0);
77
78 r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
79 if (r < 0)
80 return r;
81
82 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
83 (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
84 (*ret)->hdr->nlmsg_seq = serial;
85
86 err = NLMSG_DATA((*ret)->hdr);
87
88 err->error = error;
89
90 return 0;
91}
92
9d0db178
TG
93bool message_type_is_route(uint16_t type) {
94 switch (type) {
95 case RTM_NEWROUTE:
96 case RTM_GETROUTE:
97 case RTM_DELROUTE:
98 return true;
99 default:
100 return false;
101 }
102}
103
104bool message_type_is_link(uint16_t type) {
105 switch (type) {
106 case RTM_NEWLINK:
107 case RTM_SETLINK:
108 case RTM_GETLINK:
109 case RTM_DELLINK:
110 return true;
111 default:
112 return false;
113 }
114}
115
116bool message_type_is_addr(uint16_t type) {
117 switch (type) {
118 case RTM_NEWADDR:
119 case RTM_GETADDR:
120 case RTM_DELADDR:
121 return true;
122 default:
123 return false;
124 }
125}
126
1f01fb4f
TG
127int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
128 struct rtmsg *rtm;
129
130 rtm = NLMSG_DATA(m->hdr);
131
132 rtm->rtm_dst_len = prefixlen;
133
134 return 0;
135}
136
03d7e632 137int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
1f01fb4f 138 sd_rtnl_message **ret) {
03d7e632
TG
139 struct rtmsg *rtm;
140 int r;
141
9d0db178 142 assert_return(message_type_is_route(nlmsg_type), -EINVAL);
1f01fb4f 143 assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
03d7e632
TG
144 assert_return(ret, -EINVAL);
145
146 r = message_new(ret, NLMSG_SPACE(sizeof(struct rtmsg)));
147 if (r < 0)
148 return r;
149
150 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
151 (*ret)->hdr->nlmsg_type = nlmsg_type;
152 if (nlmsg_type == RTM_NEWROUTE)
153 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
154
155 rtm = NLMSG_DATA((*ret)->hdr);
156
0fc7531b
TG
157 (*ret)->next_rta = RTM_RTA(rtm);
158
03d7e632 159 rtm->rtm_family = rtm_family;
1f01fb4f
TG
160 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
161 rtm->rtm_type = RTN_UNICAST;
162 rtm->rtm_table = RT_TABLE_MAIN;
163 rtm->rtm_protocol = RTPROT_BOOT;
03d7e632
TG
164
165 return 0;
166}
167
fc25d7f8
TG
168int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags) {
169 struct ifinfomsg *ifi;
170
171 ifi = NLMSG_DATA(m->hdr);
172
173 ifi->ifi_flags = flags;
174
175 return 0;
176}
177
178int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
179 struct ifinfomsg *ifi;
180
181 ifi = NLMSG_DATA(m->hdr);
182
183 ifi->ifi_type = type;
184
185 return 0;
186}
187
188int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **ret) {
65f568bb
TG
189 struct ifinfomsg *ifi;
190 int r;
191
9d0db178 192 assert_return(message_type_is_link(nlmsg_type), -EINVAL);
33125ac5 193 assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
65f568bb
TG
194 assert_return(ret, -EINVAL);
195
196 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifinfomsg)));
197 if (r < 0)
198 return r;
199
200 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
201 (*ret)->hdr->nlmsg_type = nlmsg_type;
33125ac5
TG
202 if (nlmsg_type == RTM_NEWLINK)
203 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE;
65f568bb 204
dabfa9d1 205 ifi = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
206
207 ifi->ifi_family = AF_UNSPEC;
208 ifi->ifi_index = index;
65f568bb
TG
209 ifi->ifi_change = 0xffffffff;
210
0fc7531b
TG
211 (*ret)->next_rta = IFLA_RTA(ifi);
212
65f568bb
TG
213 return 0;
214}
215
dabfa9d1 216int 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
217 struct ifaddrmsg *ifa;
218 int r;
219
9d0db178 220 assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
65f568bb
TG
221 assert_return(index > 0, -EINVAL);
222 assert_return(ret, -EINVAL);
223
224 r = message_new(ret, NLMSG_SPACE(sizeof(struct ifaddrmsg)));
225 if (r < 0)
226 return r;
227
228 (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
229 (*ret)->hdr->nlmsg_type = nlmsg_type;
230
dabfa9d1 231 ifa = NLMSG_DATA((*ret)->hdr);
65f568bb
TG
232
233 ifa->ifa_family = family;
234 ifa->ifa_prefixlen = prefixlen;
235 ifa->ifa_flags = flags;
236 ifa->ifa_scope = scope;
237 ifa->ifa_index = index;
238
0fc7531b
TG
239 (*ret)->next_rta = IFA_RTA(ifa);
240
65f568bb
TG
241 return 0;
242}
243
244sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
245 if (m)
246 assert_se(REFCNT_INC(m->n_ref) >= 2);
247
248 return m;
249}
250
251sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
252 if (m && REFCNT_DEC(m->n_ref) <= 0) {
253 free(m->hdr);
254 free(m);
255 }
256
257 return NULL;
258}
259
dabfa9d1 260int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
65f568bb
TG
261 assert_return(m, -EINVAL);
262 assert_return(type, -EINVAL);
263
264 *type = m->hdr->nlmsg_type;
265
266 return 0;
267}
268
33125ac5
TG
269int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
270 struct ifinfomsg *ifi;
271
272 assert_return(m, -EINVAL);
9d0db178
TG
273 assert_return(m->hdr, -EINVAL);
274 assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
33125ac5 275 assert_return(ifindex, -EINVAL);
33125ac5
TG
276
277 ifi = NLMSG_DATA(m->hdr);
278
279 *ifindex = ifi->ifi_index;
280
281 return 0;
282}
283
50b3c42f
TG
284int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
285 struct ifinfomsg *ifi;
286
287 assert_return(m, -EINVAL);
9d0db178
TG
288 assert_return(m->hdr, -EINVAL);
289 assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
50b3c42f 290 assert_return(flags, -EINVAL);
50b3c42f
TG
291
292 ifi = NLMSG_DATA(m->hdr);
293
294 *flags = ifi->ifi_flags;
295
296 return 0;
297}
298
65f568bb
TG
299/* If successful the updated message will be correctly aligned, if unsuccessful the old message is
300 untouched */
301static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, size_t data_length) {
dabfa9d1 302 uint32_t rta_length, message_length;
65f568bb
TG
303 struct nlmsghdr *new_hdr;
304 struct rtattr *rta;
8e337e64 305 char *padding;
65f568bb 306
33125ac5
TG
307 assert(m);
308 assert(m->hdr);
309 assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
310 assert(!data || data_length > 0);
65f568bb 311
8e337e64 312 /* get the size of the new rta attribute (with padding at the end) */
65f568bb 313 rta_length = RTA_LENGTH(data_length);
8e337e64 314 /* get the new message size (with padding at the end)
65f568bb
TG
315 */
316 message_length = m->hdr->nlmsg_len + RTA_ALIGN(rta_length);
317
318 /* realloc to fit the new attribute */
319 new_hdr = realloc(m->hdr, message_length);
320 if (!new_hdr)
321 return -ENOMEM;
0fc7531b
TG
322 /* update the location of the next rta for reading */
323 m->next_rta = (struct rtattr *) ((uint8_t *) m->next_rta +
324 ((uint8_t *) new_hdr -
325 (uint8_t *) m->hdr));
65f568bb
TG
326 m->hdr = new_hdr;
327
328 /* get pointer to the attribute we are about to add */
329 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
330 /* update message size */
331 m->hdr->nlmsg_len = message_length;
332
33125ac5
TG
333 /* we are inside a container, extend it */
334 if (m->current_container)
0fc7531b 335 m->current_container->rta_len = (uint8_t *) m->hdr +
33125ac5 336 m->hdr->nlmsg_len -
0fc7531b 337 (uint8_t *) m->current_container;
33125ac5 338
65f568bb
TG
339 /* fill in the attribute */
340 rta->rta_type = type;
341 rta->rta_len = rta_length;
33125ac5
TG
342 if (!data) {
343 /* this is a container, set pointer */
344 m->current_container = rta;
345 } else {
346 /* we don't deal with the case where the user lies about the type
347 * and gives us too little data (so don't do that)
348 */
349 padding = mempcpy(RTA_DATA(rta), data, data_length);
350 /* make sure also the padding at the end of the message is initialized */
0fc7531b 351 memset(padding, '\0', (uint8_t *) m->hdr +
33125ac5 352 m->hdr->nlmsg_len -
0fc7531b 353 (uint8_t *) padding);
33125ac5 354 }
65f568bb
TG
355
356 return 0;
357}
358
0a0dc69b 359int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const char *data) {
dabfa9d1 360 uint16_t rtm_type;
0a0dc69b 361 int r;
65f568bb
TG
362
363 assert_return(m, -EINVAL);
364 assert_return(data, -EINVAL);
365
0a0dc69b
TG
366 r = sd_rtnl_message_get_type(m, &rtm_type);
367 if (r < 0)
368 return r;
65f568bb 369
0a0dc69b
TG
370 /* check that the type is correct */
371 switch (rtm_type) {
372 case RTM_NEWLINK:
373 case RTM_SETLINK:
374 case RTM_GETLINK:
375 case RTM_DELLINK:
376 if (m->current_container) {
377 if (m->current_container->rta_type != IFLA_LINKINFO ||
378 type != IFLA_INFO_KIND)
379 return -ENOTSUP;
380 } else {
381 switch (type) {
382 case IFLA_IFNAME:
383 case IFLA_IFALIAS:
384 case IFLA_QDISC:
385 break;
33125ac5
TG
386 default:
387 return -ENOTSUP;
388 }
0a0dc69b
TG
389 }
390 break;
391 case RTM_NEWADDR:
392 case RTM_GETADDR:
393 case RTM_DELADDR:
394 if (type != IFA_LABEL)
33125ac5 395 return -ENOTSUP;
0a0dc69b
TG
396 break;
397 default:
398 return -ENOTSUP;
33125ac5
TG
399 }
400
0a0dc69b
TG
401 r = add_rtattr(m, type, data, strlen(data) + 1);
402 if (r < 0)
403 return r;
404
405 return 0;
406}
407
408int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t data) {
409 uint16_t rtm_type;
410 int r;
411
412 assert_return(m, -EINVAL);
413
414 r = sd_rtnl_message_get_type(m, &rtm_type);
415 if (r < 0)
416 return r;
417
418 /* check that the type is correct */
65f568bb
TG
419 switch (rtm_type) {
420 case RTM_NEWLINK:
d2df0d0e 421 case RTM_SETLINK:
65f568bb 422 case RTM_GETLINK:
0a0dc69b 423 case RTM_DELLINK:
65f568bb 424 switch (type) {
46fabae6 425 case IFLA_MASTER:
65f568bb 426 case IFLA_MTU:
65f568bb 427 case IFLA_LINK:
0a0dc69b 428 break;
65f568bb
TG
429 default:
430 return -ENOTSUP;
431 }
0a0dc69b
TG
432 break;
433 case RTM_NEWROUTE:
434 case RTM_GETROUTE:
435 case RTM_DELROUTE:
436 switch (type) {
437 case RTA_TABLE:
438 case RTA_PRIORITY:
439 case RTA_IIF:
440 case RTA_OIF:
441 break;
442 default:
443 return -ENOTSUP;
444 }
445 break;
446 default:
447 return -ENOTSUP;
448 }
449
4d47756b 450 r = add_rtattr(m, type, &data, sizeof(uint32_t));
0a0dc69b
TG
451 if (r < 0)
452 return r;
453
454 return 0;
455}
456
457int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, const struct in_addr *data) {
458 struct ifaddrmsg *ifa;
459 struct rtmsg *rtm;
460 uint16_t rtm_type;
461 int r;
462
463 assert_return(m, -EINVAL);
464 assert_return(data, -EINVAL);
465
466 r = sd_rtnl_message_get_type(m, &rtm_type);
467 if (r < 0)
468 return r;
469
470 /* check that the type is correct */
471 switch (rtm_type) {
65f568bb 472 case RTM_NEWADDR:
65f568bb 473 case RTM_GETADDR:
0a0dc69b 474 case RTM_DELADDR:
65f568bb 475 switch (type) {
65f568bb
TG
476 case IFA_ADDRESS:
477 case IFA_LOCAL:
478 case IFA_BROADCAST:
479 case IFA_ANYCAST:
480 ifa = NLMSG_DATA(m->hdr);
0a0dc69b
TG
481
482 if (ifa->ifa_family != AF_INET)
483 return -EINVAL;
484
485 break;
65f568bb
TG
486 default:
487 return -ENOTSUP;
488 }
0a0dc69b 489 break;
03d7e632 490 case RTM_NEWROUTE:
0a0dc69b 491 case RTM_GETROUTE:
03d7e632 492 case RTM_DELROUTE:
0a0dc69b
TG
493 switch (type) {
494 case RTA_DST:
495 case RTA_SRC:
496 case RTA_GATEWAY:
497 rtm = NLMSG_DATA(m->hdr);
498
499 if (rtm->rtm_family != AF_INET)
500 return -EINVAL;
501
502 break;
503 default:
504 return -ENOTSUP;
505 }
506 break;
507 default:
508 return -ENOTSUP;
509 }
510
4d47756b 511 r = add_rtattr(m, type, data, sizeof(struct in_addr));
0a0dc69b
TG
512 if (r < 0)
513 return r;
514
515 return 0;
516}
517
518int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) {
519 struct ifaddrmsg *ifa;
520 struct rtmsg *rtm;
521 uint16_t rtm_type;
522 int r;
523
524 assert_return(m, -EINVAL);
525 assert_return(data, -EINVAL);
526
527 r = sd_rtnl_message_get_type(m, &rtm_type);
528 if (r < 0)
529 return r;
530
531 /* check that the type is correct */
532 switch (rtm_type) {
533 case RTM_NEWADDR:
534 case RTM_GETADDR:
535 case RTM_DELADDR:
536 switch (type) {
537 case IFA_ADDRESS:
538 case IFA_LOCAL:
539 case IFA_BROADCAST:
540 case IFA_ANYCAST:
541 ifa = NLMSG_DATA(m->hdr);
542
543 if (ifa->ifa_family != AF_INET6)
544 return -EINVAL;
545
546 break;
547 default:
548 return -ENOTSUP;
549 }
550 break;
551 case RTM_NEWROUTE:
03d7e632 552 case RTM_GETROUTE:
0a0dc69b 553 case RTM_DELROUTE:
03d7e632
TG
554 switch (type) {
555 case RTA_DST:
556 case RTA_SRC:
557 case RTA_GATEWAY:
558 rtm = NLMSG_DATA(m->hdr);
0a0dc69b
TG
559
560 if (rtm->rtm_family != AF_INET6)
561 return -EINVAL;
562
563 break;
564 default:
565 return -ENOTSUP;
566 }
567 default:
568 return -ENOTSUP;
569 }
570
4d47756b 571 r = add_rtattr(m, type, data, sizeof(struct in6_addr));
0a0dc69b
TG
572 if (r < 0)
573 return r;
574
575 return 0;
576}
577
578int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data) {
579 uint16_t rtm_type;
580 int r;
581
582 assert_return(m, -EINVAL);
583 assert_return(data, -EINVAL);
584
585 sd_rtnl_message_get_type(m, &rtm_type);
586
587 switch (rtm_type) {
588 case RTM_NEWLINK:
589 case RTM_SETLINK:
590 case RTM_DELLINK:
591 case RTM_GETLINK:
592 switch (type) {
593 case IFLA_ADDRESS:
594 case IFLA_BROADCAST:
595 break;
03d7e632
TG
596 default:
597 return -ENOTSUP;
598 }
0a0dc69b 599 break;
65f568bb
TG
600 default:
601 return -ENOTSUP;
602 }
0a0dc69b 603
b9eaf3d1 604 r = add_rtattr(m, type, data, ETH_ALEN);
0a0dc69b
TG
605 if (r < 0)
606 return r;
607
608 return 0;
65f568bb
TG
609}
610
33125ac5
TG
611int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
612 uint16_t rtm_type;
613
65f568bb 614 assert_return(m, -EINVAL);
33125ac5
TG
615 assert_return(!m->current_container, -EINVAL);
616
617 sd_rtnl_message_get_type(m, &rtm_type);
618
9d0db178
TG
619 if (message_type_is_link(rtm_type)) {
620 if (type == IFLA_LINKINFO)
621 return add_rtattr(m, type, NULL, 0);
622 else
33125ac5 623 return -ENOTSUP;
9d0db178
TG
624 } else
625 return -ENOTSUP;
33125ac5
TG
626
627 return 0;
628}
629
630int sd_rtnl_message_close_container(sd_rtnl_message *m) {
631 assert_return(m, -EINVAL);
632 assert_return(m->current_container, -EINVAL);
633
634 m->current_container = NULL;
635
636 return 0;
637}
638
0fc7531b
TG
639int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
640 size_t remaining_size;
33125ac5
TG
641 uint16_t rtm_type;
642 int r;
643
644 assert(m);
645 assert(m->next_rta);
646 assert(type);
647 assert(data);
65f568bb 648
0fc7531b
TG
649 remaining_size = (uint8_t *) m->hdr + m->hdr->nlmsg_len - (uint8_t *) m->next_rta;
650
651 if (!RTA_OK(m->next_rta, remaining_size))
65f568bb
TG
652 return 0;
653
33125ac5
TG
654 /* make sure we don't try to read a container
655 * TODO: add support for entering containers for reading */
656 r = sd_rtnl_message_get_type(m, &rtm_type);
657 if (r < 0)
658 return r;
659
0fc7531b
TG
660 if (message_type_is_link(rtm_type) &&
661 m->next_rta->rta_type == IFLA_LINKINFO)
662 return -EINVAL;
33125ac5 663
65f568bb
TG
664 *data = RTA_DATA(m->next_rta);
665 *type = m->next_rta->rta_type;
666
0fc7531b 667 m->next_rta = RTA_NEXT(m->next_rta, remaining_size);
65f568bb
TG
668
669 return 1;
670}
671
4555ec72 672uint32_t message_get_serial(sd_rtnl_message *m) {
65f568bb 673 assert(m);
9d0db178 674 assert(m->hdr);
65f568bb
TG
675
676 return m->hdr->nlmsg_seq;
677}
678
e16bcf98 679int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
65f568bb
TG
680 struct nlmsgerr *err;
681
e16bcf98 682 assert_return(m, -EINVAL);
9d0db178 683 assert_return(m->hdr, -EINVAL);
65f568bb
TG
684
685 if (m->hdr->nlmsg_type != NLMSG_ERROR)
686 return 0;
687
688 err = NLMSG_DATA(m->hdr);
689
690 return err->error;
691}
692
693int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
9d0db178
TG
694 assert(nl);
695 assert(m);
696 assert(m->hdr);
697
65f568bb
TG
698 if (m->sealed)
699 return -EPERM;
700
701 m->hdr->nlmsg_seq = nl->serial++;
702 m->sealed = true;
703
704 return 0;
705}
706
707static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
9d0db178
TG
708 assert(rtnl);
709 assert(need);
65f568bb
TG
710
711 /* ioctl(rtnl->fd, FIONREAD, &need)
dabfa9d1
TG
712 Does not appear to work on netlink sockets. libnl uses
713 MSG_PEEK instead. I don't know if that is worth the
714 extra roundtrip.
715
716 For now we simply use the maximum message size the kernel
717 may use (NLMSG_GOODSIZE), and then realloc to the actual
718 size after reading the message (hence avoiding huge memory
719 usage in case many small messages are kept around) */
720 *need = page_size();
65f568bb
TG
721 if (*need > 8192UL)
722 *need = 8192UL;
723
724 return 0;
725}
726
727/* returns the number of bytes sent, or a negative error code */
728int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
d4bbdb77
TG
729 union {
730 struct sockaddr sa;
731 struct sockaddr_nl nl;
732 } addr = {
733 .nl.nl_family = AF_NETLINK,
734 };
65f568bb
TG
735 ssize_t k;
736
9d0db178
TG
737 assert(nl);
738 assert(m);
739 assert(m->hdr);
65f568bb
TG
740
741 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
d4bbdb77 742 0, &addr.sa, sizeof(addr));
65f568bb
TG
743 if (k < 0)
744 return (errno == EAGAIN) ? 0 : -errno;
745
746 return k;
747}
748
749/* On success, the number of bytes received is returned and *ret points to the received message
750 * which has a valid header and the correct size.
751 * If nothing useful was received 0 is returned.
752 * On failure, a negative error code is returned.
dabfa9d1 753 */
65f568bb
TG
754int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
755 sd_rtnl_message *m;
d4bbdb77
TG
756 union {
757 struct sockaddr sa;
758 struct sockaddr_nl nl;
759 } addr;
760 socklen_t addr_len;
65f568bb
TG
761 int r;
762 ssize_t k;
763 size_t need;
764
9d0db178
TG
765 assert(nl);
766 assert(ret);
65f568bb
TG
767
768 r = message_receive_need(nl, &need);
769 if (r < 0)
770 return r;
771
772 r = message_new(&m, need);
773 if (r < 0)
774 return r;
775
d4bbdb77
TG
776 addr_len = sizeof(addr);
777
65f568bb 778 k = recvfrom(nl->fd, m->hdr, need,
d4bbdb77 779 0, &addr.sa, &addr_len);
65f568bb 780 if (k < 0)
276fc066 781 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
65f568bb
TG
782 else if (k == 0)
783 k = -ECONNRESET; /* connection was closed by the kernel */
d4bbdb77
TG
784 else if (addr_len != sizeof(addr.nl) ||
785 addr.nl.nl_family != AF_NETLINK)
65f568bb 786 k = -EIO; /* not a netlink message */
d4bbdb77 787 else if (addr.nl.nl_pid != 0)
65f568bb
TG
788 k = 0; /* not from the kernel */
789 else if ((size_t) k < sizeof(struct nlmsghdr) ||
dabfa9d1 790 (size_t) k < m->hdr->nlmsg_len)
65f568bb 791 k = -EIO; /* too small (we do accept too big though) */
a02113d2
TG
792 else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
793 k = 0; /* not broadcast and not for us */
65f568bb
TG
794
795 if (k > 0)
796 switch (m->hdr->nlmsg_type) {
0fc7531b
TG
797 struct ifinfomsg *ifi;
798 struct ifaddrmsg *ifa;
799 struct rtmsg *rtm;
800
65f568bb
TG
801 /* check that the size matches the message type */
802 case NLMSG_ERROR:
803 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
804 k = -EIO;
03d7e632 805 break;
65f568bb 806 case RTM_NEWLINK:
d2df0d0e 807 case RTM_SETLINK:
65f568bb
TG
808 case RTM_DELLINK:
809 case RTM_GETLINK:
810 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
811 k = -EIO;
0fc7531b
TG
812 else {
813 ifi = NLMSG_DATA(m->hdr);
814 m->next_rta = IFLA_RTA(ifi);
815 }
03d7e632 816 break;
65f568bb
TG
817 case RTM_NEWADDR:
818 case RTM_DELADDR:
819 case RTM_GETADDR:
820 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
821 k = -EIO;
0fc7531b
TG
822 else {
823 ifa = NLMSG_DATA(m->hdr);
824 m->next_rta = IFA_RTA(ifa);
825 }
03d7e632 826 break;
3e10a9f4
TG
827 case RTM_NEWROUTE:
828 case RTM_DELROUTE:
829 case RTM_GETROUTE:
830 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
831 k = -EIO;
0fc7531b
TG
832 else {
833 rtm = NLMSG_DATA(m->hdr);
834 m->next_rta = RTM_RTA(rtm);
835 }
3e10a9f4 836 break;
65f568bb
TG
837 case NLMSG_NOOP:
838 k = 0;
03d7e632 839 break;
65f568bb
TG
840 default:
841 k = 0; /* ignoring message of unknown type */
842 }
843
844 if (k <= 0)
845 sd_rtnl_message_unref(m);
846 else {
847 /* we probably allocated way too much memory, give it back */
848 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
849 *ret = m;
850 }
851
852 return k;
853}
0fc7531b
TG
854
855int sd_rtnl_message_rewind(sd_rtnl_message *m) {
856 struct ifinfomsg *ifi;
857 struct ifaddrmsg *ifa;
858 struct rtmsg *rtm;
859
860 assert_return(m, -EINVAL);
861 assert_return(m->hdr, -EINVAL);
862
863 switch(m->hdr->nlmsg_type) {
864 case RTM_NEWLINK:
865 case RTM_SETLINK:
866 case RTM_GETLINK:
867 case RTM_DELLINK:
868 ifi = NLMSG_DATA(m->hdr);
869
870 m->next_rta = IFLA_RTA(ifi);
871 break;
872 case RTM_NEWADDR:
873 case RTM_GETADDR:
874 case RTM_DELADDR:
875 ifa = NLMSG_DATA(m->hdr);
876
877 m->next_rta = IFA_RTA(ifa);
878 break;
879 case RTM_NEWROUTE:
880 case RTM_GETROUTE:
881 case RTM_DELROUTE:
882 rtm = NLMSG_DATA(m->hdr);
883
884 m->next_rta = RTM_RTA(rtm);
885 break;
886 default:
887 return -ENOTSUP;
888 }
889
890 return 0;
891}