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