]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-rtnl/rtnl-message.c
DEFINE_STRING_TABLE_LOOKUP: return _INVALID_* rather than assert on NULL string
[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
407int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_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 */
65f568bb
TG
418 switch (rtm_type) {
419 case RTM_NEWLINK:
d2df0d0e 420 case RTM_SETLINK:
65f568bb 421 case RTM_GETLINK:
0a0dc69b 422 case RTM_DELLINK:
65f568bb 423 switch (type) {
46fabae6 424 case IFLA_MASTER:
65f568bb 425 case IFLA_MTU:
65f568bb 426 case IFLA_LINK:
0a0dc69b 427 break;
65f568bb
TG
428 default:
429 return -ENOTSUP;
430 }
0a0dc69b
TG
431 break;
432 case RTM_NEWROUTE:
433 case RTM_GETROUTE:
434 case RTM_DELROUTE:
435 switch (type) {
436 case RTA_TABLE:
437 case RTA_PRIORITY:
438 case RTA_IIF:
439 case RTA_OIF:
440 break;
441 default:
442 return -ENOTSUP;
443 }
444 break;
445 default:
446 return -ENOTSUP;
447 }
448
4d47756b 449 r = add_rtattr(m, type, &data, sizeof(uint32_t));
0a0dc69b
TG
450 if (r < 0)
451 return r;
452
453 return 0;
454}
455
456int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, const struct in_addr *data) {
457 struct ifaddrmsg *ifa;
458 struct rtmsg *rtm;
459 uint16_t rtm_type;
460 int r;
461
462 assert_return(m, -EINVAL);
463 assert_return(data, -EINVAL);
464
465 r = sd_rtnl_message_get_type(m, &rtm_type);
466 if (r < 0)
467 return r;
468
469 /* check that the type is correct */
470 switch (rtm_type) {
65f568bb 471 case RTM_NEWADDR:
65f568bb 472 case RTM_GETADDR:
0a0dc69b 473 case RTM_DELADDR:
65f568bb 474 switch (type) {
65f568bb
TG
475 case IFA_ADDRESS:
476 case IFA_LOCAL:
477 case IFA_BROADCAST:
478 case IFA_ANYCAST:
479 ifa = NLMSG_DATA(m->hdr);
0a0dc69b
TG
480
481 if (ifa->ifa_family != AF_INET)
482 return -EINVAL;
483
484 break;
65f568bb
TG
485 default:
486 return -ENOTSUP;
487 }
0a0dc69b 488 break;
03d7e632 489 case RTM_NEWROUTE:
0a0dc69b 490 case RTM_GETROUTE:
03d7e632 491 case RTM_DELROUTE:
0a0dc69b
TG
492 switch (type) {
493 case RTA_DST:
494 case RTA_SRC:
495 case RTA_GATEWAY:
496 rtm = NLMSG_DATA(m->hdr);
497
498 if (rtm->rtm_family != AF_INET)
499 return -EINVAL;
500
501 break;
502 default:
503 return -ENOTSUP;
504 }
505 break;
506 default:
507 return -ENOTSUP;
508 }
509
4d47756b 510 r = add_rtattr(m, type, data, sizeof(struct in_addr));
0a0dc69b
TG
511 if (r < 0)
512 return r;
513
514 return 0;
515}
516
517int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) {
518 struct ifaddrmsg *ifa;
519 struct rtmsg *rtm;
520 uint16_t rtm_type;
521 int r;
522
523 assert_return(m, -EINVAL);
524 assert_return(data, -EINVAL);
525
526 r = sd_rtnl_message_get_type(m, &rtm_type);
527 if (r < 0)
528 return r;
529
530 /* check that the type is correct */
531 switch (rtm_type) {
532 case RTM_NEWADDR:
533 case RTM_GETADDR:
534 case RTM_DELADDR:
535 switch (type) {
536 case IFA_ADDRESS:
537 case IFA_LOCAL:
538 case IFA_BROADCAST:
539 case IFA_ANYCAST:
540 ifa = NLMSG_DATA(m->hdr);
541
542 if (ifa->ifa_family != AF_INET6)
543 return -EINVAL;
544
545 break;
546 default:
547 return -ENOTSUP;
548 }
549 break;
550 case RTM_NEWROUTE:
03d7e632 551 case RTM_GETROUTE:
0a0dc69b 552 case RTM_DELROUTE:
03d7e632
TG
553 switch (type) {
554 case RTA_DST:
555 case RTA_SRC:
556 case RTA_GATEWAY:
557 rtm = NLMSG_DATA(m->hdr);
0a0dc69b
TG
558
559 if (rtm->rtm_family != AF_INET6)
560 return -EINVAL;
561
562 break;
563 default:
564 return -ENOTSUP;
565 }
566 default:
567 return -ENOTSUP;
568 }
569
4d47756b 570 r = add_rtattr(m, type, data, sizeof(struct in6_addr));
0a0dc69b
TG
571 if (r < 0)
572 return r;
573
574 return 0;
575}
576
577int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data) {
578 uint16_t rtm_type;
579 int r;
580
581 assert_return(m, -EINVAL);
582 assert_return(data, -EINVAL);
583
584 sd_rtnl_message_get_type(m, &rtm_type);
585
586 switch (rtm_type) {
587 case RTM_NEWLINK:
588 case RTM_SETLINK:
589 case RTM_DELLINK:
590 case RTM_GETLINK:
591 switch (type) {
592 case IFLA_ADDRESS:
593 case IFLA_BROADCAST:
594 break;
03d7e632
TG
595 default:
596 return -ENOTSUP;
597 }
0a0dc69b 598 break;
65f568bb
TG
599 default:
600 return -ENOTSUP;
601 }
0a0dc69b 602
b9eaf3d1 603 r = add_rtattr(m, type, data, ETH_ALEN);
0a0dc69b
TG
604 if (r < 0)
605 return r;
606
607 return 0;
65f568bb
TG
608}
609
33125ac5
TG
610int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
611 uint16_t rtm_type;
612
65f568bb 613 assert_return(m, -EINVAL);
4ebe732c 614 assert_return(!CURRENT_CONTAINER(m), -EINVAL);
33125ac5
TG
615
616 sd_rtnl_message_get_type(m, &rtm_type);
617
9d0db178
TG
618 if (message_type_is_link(rtm_type)) {
619 if (type == IFLA_LINKINFO)
620 return add_rtattr(m, type, NULL, 0);
621 else
33125ac5 622 return -ENOTSUP;
9d0db178
TG
623 } else
624 return -ENOTSUP;
33125ac5
TG
625
626 return 0;
627}
628
629int sd_rtnl_message_close_container(sd_rtnl_message *m) {
630 assert_return(m, -EINVAL);
4ebe732c 631 assert_return(CURRENT_CONTAINER(m), -EINVAL);
33125ac5 632
4ebe732c 633 m->container_offset = 0;
33125ac5
TG
634
635 return 0;
636}
637
0fc7531b
TG
638int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data) {
639 size_t remaining_size;
33125ac5
TG
640 uint16_t rtm_type;
641 int r;
642
643 assert(m);
4ebe732c 644 assert(m->next_rta_offset);
33125ac5
TG
645 assert(type);
646 assert(data);
65f568bb 647
4ebe732c 648 remaining_size = m->hdr->nlmsg_len - m->next_rta_offset;
0fc7531b 649
4ebe732c 650 if (!RTA_OK(NEXT_RTA(m), remaining_size))
65f568bb
TG
651 return 0;
652
33125ac5
TG
653 /* make sure we don't try to read a container
654 * TODO: add support for entering containers for reading */
655 r = sd_rtnl_message_get_type(m, &rtm_type);
656 if (r < 0)
657 return r;
658
0fc7531b 659 if (message_type_is_link(rtm_type) &&
4ebe732c 660 NEXT_RTA(m)->rta_type == IFLA_LINKINFO)
0fc7531b 661 return -EINVAL;
33125ac5 662
4ebe732c
ZJS
663 *data = RTA_DATA(NEXT_RTA(m));
664 *type = NEXT_RTA(m)->rta_type;
65f568bb 665
4ebe732c 666 UPDATE_RTA(m, RTA_NEXT(NEXT_RTA(m), remaining_size));
65f568bb
TG
667
668 return 1;
669}
670
4555ec72 671uint32_t message_get_serial(sd_rtnl_message *m) {
65f568bb 672 assert(m);
9d0db178 673 assert(m->hdr);
65f568bb
TG
674
675 return m->hdr->nlmsg_seq;
676}
677
e16bcf98 678int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
65f568bb
TG
679 struct nlmsgerr *err;
680
e16bcf98 681 assert_return(m, -EINVAL);
9d0db178 682 assert_return(m->hdr, -EINVAL);
65f568bb
TG
683
684 if (m->hdr->nlmsg_type != NLMSG_ERROR)
685 return 0;
686
687 err = NLMSG_DATA(m->hdr);
688
689 return err->error;
690}
691
692int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
9d0db178
TG
693 assert(nl);
694 assert(m);
695 assert(m->hdr);
696
65f568bb
TG
697 if (m->sealed)
698 return -EPERM;
699
700 m->hdr->nlmsg_seq = nl->serial++;
701 m->sealed = true;
702
703 return 0;
704}
705
706static int message_receive_need(sd_rtnl *rtnl, size_t *need) {
9d0db178
TG
707 assert(rtnl);
708 assert(need);
65f568bb
TG
709
710 /* ioctl(rtnl->fd, FIONREAD, &need)
dabfa9d1
TG
711 Does not appear to work on netlink sockets. libnl uses
712 MSG_PEEK instead. I don't know if that is worth the
713 extra roundtrip.
714
715 For now we simply use the maximum message size the kernel
716 may use (NLMSG_GOODSIZE), and then realloc to the actual
717 size after reading the message (hence avoiding huge memory
718 usage in case many small messages are kept around) */
719 *need = page_size();
65f568bb
TG
720 if (*need > 8192UL)
721 *need = 8192UL;
722
723 return 0;
724}
725
726/* returns the number of bytes sent, or a negative error code */
727int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) {
d4bbdb77
TG
728 union {
729 struct sockaddr sa;
730 struct sockaddr_nl nl;
731 } addr = {
732 .nl.nl_family = AF_NETLINK,
733 };
65f568bb
TG
734 ssize_t k;
735
9d0db178
TG
736 assert(nl);
737 assert(m);
738 assert(m->hdr);
65f568bb
TG
739
740 k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len,
d4bbdb77 741 0, &addr.sa, sizeof(addr));
65f568bb
TG
742 if (k < 0)
743 return (errno == EAGAIN) ? 0 : -errno;
744
745 return k;
746}
747
748/* On success, the number of bytes received is returned and *ret points to the received message
749 * which has a valid header and the correct size.
750 * If nothing useful was received 0 is returned.
751 * On failure, a negative error code is returned.
dabfa9d1 752 */
65f568bb
TG
753int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) {
754 sd_rtnl_message *m;
d4bbdb77
TG
755 union {
756 struct sockaddr sa;
757 struct sockaddr_nl nl;
758 } addr;
759 socklen_t addr_len;
65f568bb
TG
760 int r;
761 ssize_t k;
762 size_t need;
763
9d0db178
TG
764 assert(nl);
765 assert(ret);
65f568bb
TG
766
767 r = message_receive_need(nl, &need);
768 if (r < 0)
769 return r;
770
771 r = message_new(&m, need);
772 if (r < 0)
773 return r;
774
d4bbdb77
TG
775 addr_len = sizeof(addr);
776
65f568bb 777 k = recvfrom(nl->fd, m->hdr, need,
d4bbdb77 778 0, &addr.sa, &addr_len);
65f568bb 779 if (k < 0)
276fc066 780 k = (errno == EAGAIN) ? 0 : -errno; /* no data */
65f568bb
TG
781 else if (k == 0)
782 k = -ECONNRESET; /* connection was closed by the kernel */
d4bbdb77
TG
783 else if (addr_len != sizeof(addr.nl) ||
784 addr.nl.nl_family != AF_NETLINK)
65f568bb 785 k = -EIO; /* not a netlink message */
d4bbdb77 786 else if (addr.nl.nl_pid != 0)
65f568bb
TG
787 k = 0; /* not from the kernel */
788 else if ((size_t) k < sizeof(struct nlmsghdr) ||
dabfa9d1 789 (size_t) k < m->hdr->nlmsg_len)
65f568bb 790 k = -EIO; /* too small (we do accept too big though) */
a02113d2
TG
791 else if (m->hdr->nlmsg_pid && m->hdr->nlmsg_pid != nl->sockaddr.nl.nl_pid)
792 k = 0; /* not broadcast and not for us */
65f568bb
TG
793
794 if (k > 0)
795 switch (m->hdr->nlmsg_type) {
0fc7531b
TG
796 struct ifinfomsg *ifi;
797 struct ifaddrmsg *ifa;
798 struct rtmsg *rtm;
799
65f568bb
TG
800 /* check that the size matches the message type */
801 case NLMSG_ERROR:
802 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
803 k = -EIO;
03d7e632 804 break;
65f568bb 805 case RTM_NEWLINK:
d2df0d0e 806 case RTM_SETLINK:
65f568bb
TG
807 case RTM_DELLINK:
808 case RTM_GETLINK:
809 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg)))
810 k = -EIO;
0fc7531b
TG
811 else {
812 ifi = NLMSG_DATA(m->hdr);
4ebe732c 813 UPDATE_RTA(m, IFLA_RTA(ifi));
0fc7531b 814 }
03d7e632 815 break;
65f568bb
TG
816 case RTM_NEWADDR:
817 case RTM_DELADDR:
818 case RTM_GETADDR:
819 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg)))
820 k = -EIO;
0fc7531b
TG
821 else {
822 ifa = NLMSG_DATA(m->hdr);
4ebe732c 823 UPDATE_RTA(m, IFA_RTA(ifa));
0fc7531b 824 }
03d7e632 825 break;
3e10a9f4
TG
826 case RTM_NEWROUTE:
827 case RTM_DELROUTE:
828 case RTM_GETROUTE:
829 if (m->hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtmsg)))
830 k = -EIO;
0fc7531b
TG
831 else {
832 rtm = NLMSG_DATA(m->hdr);
4ebe732c 833 UPDATE_RTA(m, RTM_RTA(rtm));
0fc7531b 834 }
3e10a9f4 835 break;
65f568bb
TG
836 case NLMSG_NOOP:
837 k = 0;
03d7e632 838 break;
65f568bb
TG
839 default:
840 k = 0; /* ignoring message of unknown type */
841 }
842
843 if (k <= 0)
844 sd_rtnl_message_unref(m);
845 else {
846 /* we probably allocated way too much memory, give it back */
847 m->hdr = realloc(m->hdr, m->hdr->nlmsg_len);
848 *ret = m;
849 }
850
851 return k;
852}
0fc7531b
TG
853
854int sd_rtnl_message_rewind(sd_rtnl_message *m) {
855 struct ifinfomsg *ifi;
856 struct ifaddrmsg *ifa;
857 struct rtmsg *rtm;
858
859 assert_return(m, -EINVAL);
860 assert_return(m->hdr, -EINVAL);
861
862 switch(m->hdr->nlmsg_type) {
863 case RTM_NEWLINK:
864 case RTM_SETLINK:
865 case RTM_GETLINK:
866 case RTM_DELLINK:
867 ifi = NLMSG_DATA(m->hdr);
4ebe732c 868 UPDATE_RTA(m, IFLA_RTA(ifi));
0fc7531b 869
0fc7531b
TG
870 break;
871 case RTM_NEWADDR:
872 case RTM_GETADDR:
873 case RTM_DELADDR:
874 ifa = NLMSG_DATA(m->hdr);
4ebe732c 875 UPDATE_RTA(m, IFA_RTA(ifa));
0fc7531b 876
0fc7531b
TG
877 break;
878 case RTM_NEWROUTE:
879 case RTM_GETROUTE:
880 case RTM_DELROUTE:
881 rtm = NLMSG_DATA(m->hdr);
4ebe732c 882 UPDATE_RTA(m, RTM_RTA(rtm));
0fc7531b 883
0fc7531b
TG
884 break;
885 default:
886 return -ENOTSUP;
887 }
888
889 return 0;
890}