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