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