]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-message.c
network: set MTU after IPoIB configs are applied
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-message.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
65f568bb
TG
2
3#include <netinet/in.h>
65f568bb
TG
4#include <stdbool.h>
5#include <unistd.h>
6
07630cea
LP
7#include "sd-netlink.h"
8
b5efdb8a 9#include "alloc-util.h"
f97b34a6 10#include "format-util.h"
8f3c1859 11#include "memory-util.h"
1c4baffc
TG
12#include "netlink-internal.h"
13#include "netlink-types.h"
07630cea 14#include "netlink-util.h"
07630cea 15#include "socket-util.h"
8f3c1859 16#include "strv.h"
65f568bb 17
179b4db4 18#define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset))
4ebe732c 19
0a2478a9 20#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
4c641e99 21#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
0a2478a9 22
409856d3 23int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
1c4baffc 24 sd_netlink_message *m;
65f568bb 25
1cedca05
YW
26 assert(nl);
27 assert(ret);
65f568bb 28
409856d3
YW
29 /* Note that 'nl' is currently unused, if we start using it internally we must take care to
30 * avoid problems due to mutual references between buses and their queued messages. See sd-bus. */
8c578303 31
6bf8e24b 32 m = new(sd_netlink_message, 1);
65f568bb
TG
33 if (!m)
34 return -ENOMEM;
35
6bf8e24b
YW
36 *m = (sd_netlink_message) {
37 .n_ref = 1,
409856d3 38 .protocol = nl->protocol,
6bf8e24b
YW
39 .sealed = false,
40 };
65f568bb
TG
41
42 *ret = m;
65f568bb
TG
43 return 0;
44}
45
1cedca05
YW
46int message_new_full(
47 sd_netlink *nl,
48 uint16_t nlmsg_type,
49 const NLTypeSystem *type_system,
50 size_t header_size,
51 sd_netlink_message **ret) {
52
4afd3348 53 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
d8e538ec
TG
54 size_t size;
55 int r;
56
1cedca05
YW
57 assert(nl);
58 assert(type_system);
59 assert(ret);
d8e538ec 60
1cedca05
YW
61 size = NLMSG_SPACE(header_size);
62 assert(size >= sizeof(struct nlmsghdr));
12b7dff4 63
409856d3 64 r = message_new_empty(nl, &m);
d8e538ec
TG
65 if (r < 0)
66 return r;
67
1cedca05 68 m->containers[0].type_system = type_system;
1b89cf56 69
1b89cf56
TG
70 m->hdr = malloc0(size);
71 if (!m->hdr)
72 return -ENOMEM;
73
74 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
1b89cf56 75 m->hdr->nlmsg_len = size;
1cedca05 76 m->hdr->nlmsg_type = nlmsg_type;
1b89cf56 77
1cc6c93a 78 *ret = TAKE_PTR(m);
d8e538ec
TG
79 return 0;
80}
81
1cedca05 82int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
56fdc16d
YW
83 const NLTypeSystem *type_system;
84 size_t size;
1cedca05
YW
85 int r;
86
87 assert_return(nl, -EINVAL);
88 assert_return(ret, -EINVAL);
89
56fdc16d 90 r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size);
1cedca05
YW
91 if (r < 0)
92 return r;
93
56fdc16d 94 return message_new_full(nl, type, type_system, size, ret);
1cedca05
YW
95}
96
409856d3
YW
97int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
98 struct nlmsgerr *err;
99 int r;
100
101 assert(error <= 0);
102
103 r = message_new(nl, ret, NLMSG_ERROR);
104 if (r < 0)
105 return r;
106
107 message_seal(*ret);
108 (*ret)->hdr->nlmsg_seq = serial;
109
110 err = NLMSG_DATA((*ret)->hdr);
111 err->error = error;
112
113 return 0;
114}
115
1c4baffc 116int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
6e20c8f8
TG
117 assert_return(m, -EINVAL);
118 assert_return(m->hdr, -EINVAL);
ec44d3f4
YW
119 assert_return(m->protocol != NETLINK_ROUTE ||
120 IN_SET(m->hdr->nlmsg_type,
ffeb16f5 121 RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
c16c7808 122 RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL);
6e20c8f8 123
5883ff60 124 SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);
6e20c8f8
TG
125
126 return 0;
127}
128
f23ab4dc 129DEFINE_TRIVIAL_REF_FUNC(sd_netlink_message, sd_netlink_message);
65f568bb 130
1c4baffc 131sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) {
f23ab4dc 132 while (m && --m->n_ref == 0) {
3dd215e0
TG
133 unsigned i;
134
65f568bb 135 free(m->hdr);
3dd215e0 136
9f5bbfe3 137 for (i = 0; i <= m->n_containers; i++)
f663aeb8 138 free(m->containers[i].attributes);
3dd215e0 139
f23ab4dc 140 sd_netlink_message *t = m;
82e4eda6
DH
141 m = m->next;
142 free(t);
65f568bb
TG
143 }
144
145 return NULL;
146}
147
041ea9f9 148int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
65f568bb
TG
149 assert_return(m, -EINVAL);
150 assert_return(type, -EINVAL);
151
152 *type = m->hdr->nlmsg_type;
153
154 return 0;
155}
156
c06cb593
SS
157int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) {
158 assert_return(m, -EINVAL);
159 assert_return(flags, -EINVAL);
160
161 m->hdr->nlmsg_flags = flags;
162
163 return 0;
164}
165
041ea9f9 166int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
1f0db3ed
TG
167 assert_return(m, -EINVAL);
168
4d4d898a 169 return m->multicast_group != 0;
1f0db3ed
TG
170}
171
4ebe732c
ZJS
172/* If successful the updated message will be correctly aligned, if
173 unsuccessful the old message is untouched. */
1c4baffc 174static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) {
6497a8aa 175 size_t message_length;
65f568bb
TG
176 struct nlmsghdr *new_hdr;
177 struct rtattr *rta;
7ca1d319 178 int offset;
65f568bb 179
33125ac5
TG
180 assert(m);
181 assert(m->hdr);
e5c4350b 182 assert(!m->sealed);
33125ac5 183 assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
6497a8aa 184 assert(!data || data_length > 0);
4ebe732c
ZJS
185
186 /* get the new message size (with padding at the end) */
6497a8aa 187 message_length = m->hdr->nlmsg_len + RTA_SPACE(data_length);
65f568bb 188
05d0c2e3
JT
189 /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
190 if (message_length > MIN(page_size(), 8192UL))
191 return -ENOBUFS;
192
65f568bb
TG
193 /* realloc to fit the new attribute */
194 new_hdr = realloc(m->hdr, message_length);
195 if (!new_hdr)
196 return -ENOMEM;
197 m->hdr = new_hdr;
198
199 /* get pointer to the attribute we are about to add */
6497a8aa
YW
200 rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
201
202 rtattr_append_attribute_internal(rta, type, data, data_length);
65f568bb 203
5a081409 204 /* if we are inside containers, extend them */
c7209bcf 205 for (unsigned i = 0; i < m->n_containers; i++)
6497a8aa 206 GET_CONTAINER(m, i)->rta_len += RTA_SPACE(data_length);
7ca1d319 207
4ebe732c 208 /* update message size */
6497a8aa 209 offset = m->hdr->nlmsg_len;
4ebe732c
ZJS
210 m->hdr->nlmsg_len = message_length;
211
6497a8aa 212 /* return old message size */
7ca1d319 213 return offset;
65f568bb
TG
214}
215
6c14ad61 216static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) {
d8e538ec 217 const NLType *type;
d8e538ec 218
6c14ad61
DH
219 assert(m);
220
98be4292
YW
221 type = type_system_get_type(m->containers[m->n_containers].type_system, attribute_type);
222 if (!type)
223 return -EOPNOTSUPP;
d8e538ec 224
817d1cd8 225 if (type_get_type(type) != data_type)
d8e538ec
TG
226 return -EINVAL;
227
6c14ad61
DH
228 if (out_size)
229 *out_size = type_get_size(type);
230 return 0;
d8e538ec
TG
231}
232
1c4baffc 233int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) {
d8e538ec 234 size_t length, size;
0a0dc69b 235 int r;
65f568bb
TG
236
237 assert_return(m, -EINVAL);
e5c4350b 238 assert_return(!m->sealed, -EPERM);
65f568bb
TG
239 assert_return(data, -EINVAL);
240
6c14ad61 241 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
0a0dc69b
TG
242 if (r < 0)
243 return r;
65f568bb 244
d8e538ec 245 if (size) {
3072eecf
LP
246 length = strnlen(data, size+1);
247 if (length > size)
d8e538ec
TG
248 return -EINVAL;
249 } else
250 length = strlen(data);
33125ac5 251
d8e538ec 252 r = add_rtattr(m, type, data, length + 1);
0a0dc69b
TG
253 if (r < 0)
254 return r;
255
256 return 0;
257}
258
6d725977
YW
259int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data) {
260 size_t length, size;
261 char * const *p;
262 int r;
263
264 assert_return(m, -EINVAL);
265 assert_return(!m->sealed, -EPERM);
266 assert_return(data, -EINVAL);
267
268 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
269 if (r < 0)
270 return r;
271
272 STRV_FOREACH(p, data) {
273 if (size) {
274 length = strnlen(*p, size+1);
275 if (length > size)
276 return -EINVAL;
277 } else
278 length = strlen(*p);
279
280 r = add_rtattr(m, type, *p, length + 1);
281 if (r < 0)
282 return r;
283 }
284
285 return 0;
286}
287
c06aead0
SS
288int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) {
289 size_t size;
290 int r;
291
292 assert_return(m, -EINVAL);
293 assert_return(!m->sealed, -EPERM);
294
295 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG);
296 if (r < 0)
297 return r;
298
299 r = add_rtattr(m, type, NULL, 0);
300 if (r < 0)
301 return r;
302
303 return 0;
304}
305
1c4baffc 306int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) {
7b179640
SS
307 int r;
308
309 assert_return(m, -EINVAL);
310 assert_return(!m->sealed, -EPERM);
311
6c14ad61 312 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
7b179640
SS
313 if (r < 0)
314 return r;
315
7b179640
SS
316 r = add_rtattr(m, type, &data, sizeof(uint8_t));
317 if (r < 0)
318 return r;
319
320 return 0;
321}
322
1c4baffc 323int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) {
01b36069
TG
324 int r;
325
326 assert_return(m, -EINVAL);
e5c4350b 327 assert_return(!m->sealed, -EPERM);
01b36069 328
6c14ad61 329 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
01b36069
TG
330 if (r < 0)
331 return r;
332
01b36069
TG
333 r = add_rtattr(m, type, &data, sizeof(uint16_t));
334 if (r < 0)
335 return r;
336
337 return 0;
338}
339
1c4baffc 340int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) {
0a0dc69b
TG
341 int r;
342
343 assert_return(m, -EINVAL);
e5c4350b 344 assert_return(!m->sealed, -EPERM);
0a0dc69b 345
6c14ad61 346 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
0a0dc69b
TG
347 if (r < 0)
348 return r;
349
4d47756b 350 r = add_rtattr(m, type, &data, sizeof(uint32_t));
0a0dc69b
TG
351 if (r < 0)
352 return r;
353
354 return 0;
355}
356
81962db7
SS
357int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data) {
358 int r;
359
360 assert_return(m, -EINVAL);
361 assert_return(!m->sealed, -EPERM);
362
363 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U64);
364 if (r < 0)
365 return r;
366
367 r = add_rtattr(m, type, &data, sizeof(uint64_t));
368 if (r < 0)
369 return r;
370
371 return 0;
372}
373
aa550d2a
SS
374int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) {
375 int r;
376
377 assert_return(m, -EINVAL);
378 assert_return(!m->sealed, -EPERM);
379
380 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8);
381 if (r < 0)
382 return r;
383
384 r = add_rtattr(m, type, &data, sizeof(int8_t));
385 if (r < 0)
386 return r;
387
388 return 0;
389}
390
391int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) {
392 int r;
393
394 assert_return(m, -EINVAL);
395 assert_return(!m->sealed, -EPERM);
396
397 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16);
398 if (r < 0)
399 return r;
400
401 r = add_rtattr(m, type, &data, sizeof(int16_t));
402 if (r < 0)
403 return r;
404
405 return 0;
406}
407
408int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) {
409 int r;
410
411 assert_return(m, -EINVAL);
412 assert_return(!m->sealed, -EPERM);
413
414 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32);
415 if (r < 0)
416 return r;
417
418 r = add_rtattr(m, type, &data, sizeof(int32_t));
419 if (r < 0)
420 return r;
421
422 return 0;
423}
424
425int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) {
426 int r;
427
428 assert_return(m, -EINVAL);
429 assert_return(!m->sealed, -EPERM);
430
431 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64);
432 if (r < 0)
433 return r;
434
435 r = add_rtattr(m, type, &data, sizeof(int64_t));
436 if (r < 0)
437 return r;
438
439 return 0;
440}
441
17af840b
SS
442int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
443 int r;
444
445 assert_return(m, -EINVAL);
446 assert_return(!m->sealed, -EPERM);
447
b8cc01a2 448 r = add_rtattr(m, type, data, len);
17af840b
SS
449 if (r < 0)
450 return r;
451
452 return 0;
453}
454
67b19a49 455int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data) {
0a0dc69b
TG
456 int r;
457
458 assert_return(m, -EINVAL);
e5c4350b 459 assert_return(!m->sealed, -EPERM);
0a0dc69b 460 assert_return(data, -EINVAL);
67b19a49 461 assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
0a0dc69b 462
6c14ad61 463 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
0a0dc69b
TG
464 if (r < 0)
465 return r;
466
67b19a49 467 r = add_rtattr(m, type, data, FAMILY_ADDRESS_SIZE(family));
0a0dc69b
TG
468 if (r < 0)
469 return r;
470
471 return 0;
472}
473
67b19a49
YW
474int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) {
475 return netlink_message_append_in_addr_union(m, type, AF_INET, (const union in_addr_union *) data);
476}
abd48ec8 477
67b19a49
YW
478int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) {
479 return netlink_message_append_in_addr_union(m, type, AF_INET6, (const union in_addr_union *) data);
abd48ec8
YW
480}
481
67b19a49 482int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data) {
abd48ec8
YW
483 int r;
484
485 assert_return(m, -EINVAL);
486 assert_return(!m->sealed, -EPERM);
487 assert_return(data, -EINVAL);
67b19a49 488 assert_return(IN_SET(data->sa.sa_family, AF_INET, AF_INET6), -EINVAL);
abd48ec8
YW
489
490 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR);
491 if (r < 0)
492 return r;
493
67b19a49 494 r = add_rtattr(m, type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
abd48ec8
YW
495 if (r < 0)
496 return r;
497
498 return 0;
499}
500
67b19a49
YW
501int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data) {
502 return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data);
503}
0a0dc69b 504
67b19a49
YW
505int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data) {
506 return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data);
0a0dc69b
TG
507}
508
1c4baffc 509int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) {
0a0dc69b
TG
510 int r;
511
512 assert_return(m, -EINVAL);
e5c4350b 513 assert_return(!m->sealed, -EPERM);
0a0dc69b
TG
514 assert_return(data, -EINVAL);
515
6c14ad61 516 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
d8e538ec
TG
517 if (r < 0)
518 return r;
0a0dc69b 519
b9eaf3d1 520 r = add_rtattr(m, type, data, ETH_ALEN);
0a0dc69b
TG
521 if (r < 0)
522 return r;
523
524 return 0;
65f568bb
TG
525}
526
ca2b7cd8 527int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data) {
4fc8a29a
TR
528 int r;
529
530 assert_return(m, -EINVAL);
531 assert_return(!m->sealed, -EPERM);
532 assert_return(data, -EINVAL);
533 assert_return(data->length > 0, -EINVAL);
534
535 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
536 if (r < 0)
537 return r;
538
ca2b7cd8 539 r = add_rtattr(m, type, data->bytes, data->length);
4fc8a29a
TR
540 if (r < 0)
541 return r;
542
543 return 0;
544}
545
1c4baffc 546int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) {
aba496a5
UTL
547 int r;
548
549 assert_return(m, -EINVAL);
550 assert_return(!m->sealed, -EPERM);
551 assert_return(info, -EINVAL);
552
6c14ad61 553 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
aba496a5
UTL
554 if (r < 0)
555 return r;
556
557 r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo));
558 if (r < 0)
559 return r;
560
561 return 0;
562}
563
1c4baffc 564int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) {
d8e538ec
TG
565 size_t size;
566 int r;
33125ac5 567
65f568bb 568 assert_return(m, -EINVAL);
e5c4350b 569 assert_return(!m->sealed, -EPERM);
179b4db4 570 /* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */
409856d3 571 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
33125ac5 572
6c14ad61 573 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED);
4af7b60d
TG
574 if (r < 0) {
575 const NLTypeSystemUnion *type_system_union;
576 int family;
577
6c14ad61 578 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION);
4af7b60d
TG
579 if (r < 0)
580 return r;
4af7b60d 581
89489ef7 582 r = sd_rtnl_message_get_family(m, &family);
4af7b60d
TG
583 if (r < 0)
584 return r;
585
98be4292
YW
586 type_system_union = type_system_get_type_system_union(
587 m->containers[m->n_containers].type_system,
588 type);
589 if (!type_system_union)
590 return -EOPNOTSUPP;
4af7b60d 591
98be4292
YW
592 m->containers[m->n_containers + 1].type_system =
593 type_system_union_get_type_system_by_protocol(
ea073c8f 594 type_system_union,
ea073c8f 595 family);
98be4292
YW
596 } else
597 m->containers[m->n_containers + 1].type_system =
598 type_system_get_type_system(
599 m->containers[m->n_containers].type_system,
600 type);
601 if (!m->containers[m->n_containers + 1].type_system)
602 return -EOPNOTSUPP;
31a4e153 603
fcf81a54 604 r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
d8e538ec
TG
605 if (r < 0)
606 return r;
607
313cefa1 608 m->containers[m->n_containers++].offset = r;
7ca1d319 609
d8e538ec
TG
610 return 0;
611}
612
1c4baffc 613int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) {
d8e538ec
TG
614 const NLTypeSystemUnion *type_system_union;
615 int r;
616
617 assert_return(m, -EINVAL);
618 assert_return(!m->sealed, -EPERM);
409856d3 619 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
d8e538ec 620
98be4292
YW
621 type_system_union = type_system_get_type_system_union(
622 m->containers[m->n_containers].type_system,
623 type);
624 if (!type_system_union)
625 return -EOPNOTSUPP;
d8e538ec 626
98be4292
YW
627 m->containers[m->n_containers + 1].type_system =
628 type_system_union_get_type_system_by_string(
ea073c8f 629 type_system_union,
ea073c8f 630 key);
98be4292
YW
631 if (!m->containers[m->n_containers + 1].type_system)
632 return -EOPNOTSUPP;
33125ac5 633
b019c545 634 r = sd_netlink_message_append_string(m, type_system_union_get_match_attribute(type_system_union), key);
d8e538ec
TG
635 if (r < 0)
636 return r;
637
05d0c2e3 638 /* do we ever need non-null size */
da041d69 639 r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
d8e538ec
TG
640 if (r < 0)
641 return r;
642
313cefa1 643 m->containers[m->n_containers++].offset = r;
7ca1d319 644
d8e538ec 645 return 0;
33125ac5
TG
646}
647
1c4baffc 648int sd_netlink_message_close_container(sd_netlink_message *m) {
33125ac5 649 assert_return(m, -EINVAL);
e5c4350b 650 assert_return(!m->sealed, -EPERM);
5a081409 651 assert_return(m->n_containers > 0, -EINVAL);
33125ac5 652
f663aeb8 653 m->containers[m->n_containers].type_system = NULL;
05d0c2e3
JT
654 m->containers[m->n_containers].offset = 0;
655 m->n_containers--;
656
657 return 0;
658}
659
660int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
661 int r;
662
663 assert_return(m, -EINVAL);
664 assert_return(!m->sealed, -EPERM);
409856d3 665 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
05d0c2e3
JT
666
667 r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
668 if (r < 0)
669 return r;
670
671 m->containers[m->n_containers].offset = r;
672 m->n_containers++;
673 m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
674
675 return 0;
676}
677
678int sd_netlink_message_cancel_array(sd_netlink_message *m) {
05d0c2e3
JT
679 uint32_t rta_len;
680
681 assert_return(m, -EINVAL);
682 assert_return(!m->sealed, -EPERM);
683 assert_return(m->n_containers > 1, -EINVAL);
684
685 rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len;
686
c7209bcf 687 for (unsigned i = 0; i < m->n_containers; i++)
05d0c2e3
JT
688 GET_CONTAINER(m, i)->rta_len -= rta_len;
689
690 m->hdr->nlmsg_len -= rta_len;
691
313cefa1 692 m->n_containers--;
05d0c2e3 693 m->containers[m->n_containers].type_system = NULL;
33125ac5
TG
694
695 return 0;
696}
697
3b4e3ebb
YW
698static int netlink_message_read_internal(
699 sd_netlink_message *m,
700 unsigned short type,
701 void **ret_data,
702 bool *ret_net_byteorder) {
703
f663aeb8 704 struct netlink_attribute *attribute;
f66eeb6b
TG
705 struct rtattr *rta;
706
44caa5e7
SS
707 assert_return(m, -EINVAL);
708 assert_return(m->sealed, -EPERM);
f1dd72c2 709
409856d3 710 assert(m->n_containers < NETLINK_CONTAINER_DEPTH);
e4a1e68d
YW
711
712 if (!m->containers[m->n_containers].attributes)
713 return -ENODATA;
48fb0d13 714
416e8419 715 if (type > m->containers[m->n_containers].max_attribute)
48fb0d13 716 return -ENODATA;
44caa5e7 717
f663aeb8
TG
718 attribute = &m->containers[m->n_containers].attributes[type];
719
f1dd72c2 720 if (attribute->offset == 0)
44caa5e7
SS
721 return -ENODATA;
722
f663aeb8 723 rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset);
44caa5e7 724
3b4e3ebb
YW
725 if (ret_data)
726 *ret_data = RTA_DATA(rta);
f66eeb6b 727
3b4e3ebb
YW
728 if (ret_net_byteorder)
729 *ret_net_byteorder = attribute->net_byteorder;
4c641e99 730
f66eeb6b 731 return RTA_PAYLOAD(rta);
44caa5e7
SS
732}
733
926062f0
SS
734int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data) {
735 void *attr_data;
736 int r;
737
738 assert_return(m, -EINVAL);
739
740 r = netlink_message_read_internal(m, type, &attr_data, NULL);
741 if (r < 0)
742 return r;
743
744 if ((size_t) r < size)
745 return -EIO;
746
747 if (data)
748 memcpy(data, attr_data, size);
749
5dc3dbe8 750 return r;
926062f0
SS
751}
752
de52a83c 753int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
52888279 754 void *attr_data;
de52a83c
YW
755 int r;
756
757 assert_return(m, -EINVAL);
758
759 r = netlink_message_read_internal(m, type, &attr_data, NULL);
760 if (r < 0)
761 return r;
762
763 if (ret_data) {
52888279
YW
764 void *data;
765
de52a83c 766 data = memdup(attr_data, r);
a1d2ae06
YW
767 if (!data)
768 return -ENOMEM;
769
770 *ret_data = data;
771 }
772
773 if (ret_size)
774 *ret_size = r;
775
776 return r;
777}
778
779int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
780 void *attr_data;
781 int r;
782
783 assert_return(m, -EINVAL);
784
785 r = netlink_message_read_internal(m, type, &attr_data, NULL);
786 if (r < 0)
787 return r;
788
789 if (ret_data) {
790 void *data;
791
792 data = memdup_suffix0(attr_data, r);
de52a83c
YW
793 if (!data)
794 return -ENOMEM;
795
796 *ret_data = data;
797 }
798
799 if (ret_size)
800 *ret_size = r;
801
802 return r;
803}
804
59d4103f
YW
805int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
806 void *attr_data;
59d4103f
YW
807 int r;
808
809 assert_return(m, -EINVAL);
810
811 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
812 if (r < 0)
813 return r;
814
815 r = netlink_message_read_internal(m, type, &attr_data, NULL);
816 if (r < 0)
817 return r;
818
819 if (data) {
52888279
YW
820 char *str;
821
59d4103f
YW
822 str = strndup(attr_data, r);
823 if (!str)
824 return -ENOMEM;
825
826 *data = str;
827 }
828
829 return 0;
830}
831
1c4baffc 832int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
44caa5e7 833 void *attr_data;
52888279 834 int r;
44caa5e7 835
73ae2b7d
TG
836 assert_return(m, -EINVAL);
837
6c14ad61 838 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
d8e538ec
TG
839 if (r < 0)
840 return r;
44caa5e7 841
4c641e99 842 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 843 if (r < 0)
44caa5e7 844 return r;
52888279
YW
845
846 if (strnlen(attr_data, r) >= (size_t) r)
f66eeb6b 847 return -EIO;
44caa5e7 848
73ae2b7d
TG
849 if (data)
850 *data = (const char *) attr_data;
44caa5e7
SS
851
852 return 0;
853}
854
1c4baffc 855int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
44caa5e7 856 void *attr_data;
52888279 857 int r;
44caa5e7 858
73ae2b7d
TG
859 assert_return(m, -EINVAL);
860
6c14ad61 861 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
d8e538ec
TG
862 if (r < 0)
863 return r;
44caa5e7 864
4c641e99 865 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 866 if (r < 0)
44caa5e7 867 return r;
52888279
YW
868
869 if ((size_t) r < sizeof(uint8_t))
f66eeb6b 870 return -EIO;
44caa5e7 871
73ae2b7d
TG
872 if (data)
873 *data = *(uint8_t *) attr_data;
44caa5e7
SS
874
875 return 0;
876}
877
1c4baffc 878int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
44caa5e7 879 void *attr_data;
4c641e99
TG
880 bool net_byteorder;
881 int r;
44caa5e7 882
73ae2b7d
TG
883 assert_return(m, -EINVAL);
884
6c14ad61 885 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
d8e538ec
TG
886 if (r < 0)
887 return r;
44caa5e7 888
4c641e99 889 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 890 if (r < 0)
44caa5e7 891 return r;
52888279
YW
892
893 if ((size_t) r < sizeof(uint16_t))
f66eeb6b 894 return -EIO;
44caa5e7 895
4c641e99
TG
896 if (data) {
897 if (net_byteorder)
898 *data = be16toh(*(uint16_t *) attr_data);
899 else
900 *data = *(uint16_t *) attr_data;
901 }
44caa5e7
SS
902
903 return 0;
904}
905
1c4baffc 906int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
44caa5e7 907 void *attr_data;
4c641e99
TG
908 bool net_byteorder;
909 int r;
44caa5e7 910
73ae2b7d
TG
911 assert_return(m, -EINVAL);
912
6c14ad61 913 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
d8e538ec
TG
914 if (r < 0)
915 return r;
44caa5e7 916
4c641e99 917 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 918 if (r < 0)
44caa5e7 919 return r;
52888279
YW
920
921 if ((size_t) r < sizeof(uint32_t))
f66eeb6b 922 return -EIO;
44caa5e7 923
4c641e99
TG
924 if (data) {
925 if (net_byteorder)
926 *data = be32toh(*(uint32_t *) attr_data);
927 else
928 *data = *(uint32_t *) attr_data;
929 }
44caa5e7
SS
930
931 return 0;
932}
933
1c4baffc 934int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
4e9e7f18 935 void *attr_data;
52888279 936 int r;
4e9e7f18 937
73ae2b7d
TG
938 assert_return(m, -EINVAL);
939
6c14ad61 940 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
d8e538ec
TG
941 if (r < 0)
942 return r;
4e9e7f18 943
4c641e99 944 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 945 if (r < 0)
4e9e7f18 946 return r;
52888279
YW
947
948 if ((size_t) r < sizeof(struct ether_addr))
f66eeb6b 949 return -EIO;
4e9e7f18 950
73ae2b7d
TG
951 if (data)
952 memcpy(data, attr_data, sizeof(struct ether_addr));
4e9e7f18
SS
953
954 return 0;
955}
956
ca2b7cd8 957int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) {
4fc8a29a 958 void *attr_data;
52888279 959 int r;
4fc8a29a
TR
960
961 assert_return(m, -EINVAL);
962
963 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
964 if (r < 0)
965 return r;
966
967 r = netlink_message_read_internal(m, type, &attr_data, NULL);
968 if (r < 0)
969 return r;
52888279
YW
970
971 if (r > HW_ADDR_MAX_SIZE)
4fc8a29a
TR
972 return -EIO;
973
974 if (data) {
ca2b7cd8 975 memcpy(data->bytes, attr_data, r);
4fc8a29a
TR
976 data->length = r;
977 }
978
979 return 0;
980}
981
1c4baffc 982int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
aba496a5 983 void *attr_data;
52888279 984 int r;
aba496a5 985
73ae2b7d
TG
986 assert_return(m, -EINVAL);
987
6c14ad61 988 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
aba496a5
UTL
989 if (r < 0)
990 return r;
991
4c641e99 992 r = netlink_message_read_internal(m, type, &attr_data, NULL);
aba496a5
UTL
993 if (r < 0)
994 return r;
52888279
YW
995
996 if ((size_t) r < sizeof(struct ifa_cacheinfo))
aba496a5
UTL
997 return -EIO;
998
73ae2b7d
TG
999 if (info)
1000 memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
aba496a5
UTL
1001
1002 return 0;
1003}
1004
f29b6b37 1005int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) {
4e9e7f18 1006 void *attr_data;
f29b6b37 1007 int r;
4e9e7f18 1008
73ae2b7d 1009 assert_return(m, -EINVAL);
f29b6b37 1010 assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
73ae2b7d 1011
6c14ad61 1012 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
d8e538ec
TG
1013 if (r < 0)
1014 return r;
4e9e7f18 1015
4c641e99 1016 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 1017 if (r < 0)
4e9e7f18 1018 return r;
52888279
YW
1019
1020 if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
f66eeb6b 1021 return -EIO;
4e9e7f18 1022
73ae2b7d 1023 if (data)
f29b6b37 1024 memcpy(data, attr_data, FAMILY_ADDRESS_SIZE(family));
4e9e7f18
SS
1025
1026 return 0;
1027}
1028
f29b6b37
YW
1029int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
1030 union in_addr_union u;
4e9e7f18 1031 int r;
4e9e7f18 1032
f29b6b37
YW
1033 r = netlink_message_read_in_addr_union(m, type, AF_INET, &u);
1034 if (r >= 0 && data)
1035 *data = u.in;
73ae2b7d 1036
f29b6b37
YW
1037 return r;
1038}
4e9e7f18 1039
f29b6b37
YW
1040int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
1041 union in_addr_union u;
1042 int r;
4e9e7f18 1043
f29b6b37
YW
1044 r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u);
1045 if (r >= 0 && data)
1046 *data = u.in6;
4e9e7f18 1047
f29b6b37 1048 return r;
4e9e7f18
SS
1049}
1050
3b4e3ebb
YW
1051int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type) {
1052 void *attr_data;
1053 int r;
1054
1055 assert_return(m, -EINVAL);
1056
1057 /* This returns 1 when the flag is set, 0 when not set, negative errno on error. */
1058
1059 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_FLAG);
1060 if (r < 0)
1061 return r;
1062
1063 r = netlink_message_read_internal(m, type, &attr_data, NULL);
1064 if (r == -ENODATA)
1065 return 0;
1066 if (r < 0)
1067 return r;
1068
1069 return 1;
1070}
1071
8f3c1859
YW
1072int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) {
1073 _cleanup_strv_free_ char **s = NULL;
1074 const NLTypeSystem *type_system;
1075 const NLType *nl_type;
1076 struct rtattr *rta;
1077 void *container;
f501c251 1078 size_t rt_len;
8f3c1859
YW
1079 int r;
1080
1081 assert_return(m, -EINVAL);
409856d3 1082 assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
8f3c1859 1083
98be4292
YW
1084 nl_type = type_system_get_type(
1085 m->containers[m->n_containers].type_system,
1086 container_type);
1087 if (!nl_type)
1088 return -EOPNOTSUPP;
8f3c1859
YW
1089
1090 if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
1091 return -EINVAL;
1092
98be4292
YW
1093 type_system = type_system_get_type_system(
1094 m->containers[m->n_containers].type_system,
1095 container_type);
1096 if (!type_system)
1097 return -EOPNOTSUPP;
8f3c1859 1098
98be4292
YW
1099 nl_type = type_system_get_type(type_system, type_id);
1100 if (!nl_type)
1101 return -EOPNOTSUPP;
8f3c1859
YW
1102
1103 if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
1104 return -EINVAL;
1105
1106 r = netlink_message_read_internal(m, container_type, &container, NULL);
1107 if (r < 0)
1108 return r;
1109
f501c251 1110 rt_len = (size_t) r;
8f3c1859
YW
1111 rta = container;
1112
f501c251
YW
1113 /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
1114 * LGTM.com analysis does not like the type difference. Hence, here we
1115 * introduce an unsigned short variable as a workaround. */
1116 unsigned short len = rt_len;
1117 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
8f3c1859
YW
1118 unsigned short type;
1119
1120 type = RTA_TYPE(rta);
1121 if (type != type_id)
1122 continue;
1123
1124 r = strv_extend(&s, RTA_DATA(rta));
1125 if (r < 0)
1126 return r;
1127 }
1128
1129 *ret = TAKE_PTR(s);
1130 return 0;
1131}
1132
416e8419
YW
1133static int netlink_container_parse(
1134 sd_netlink_message *m,
1135 struct netlink_container *container,
1136 struct rtattr *rta,
1137 size_t rt_len) {
1138
f663aeb8 1139 _cleanup_free_ struct netlink_attribute *attributes = NULL;
416e8419 1140 uint16_t max_attr = 0;
4203fc8b 1141
f501c251
YW
1142 /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
1143 * LGTM.com analysis does not like the type difference. Hence, here we
1144 * introduce an unsigned short variable as a workaround. */
1145 unsigned short len = rt_len;
1146 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
416e8419 1147 uint16_t attr;
f663aeb8 1148
416e8419
YW
1149 attr = RTA_TYPE(rta);
1150 max_attr = MAX(max_attr, attr);
4203fc8b 1151
416e8419 1152 if (!GREEDY_REALLOC0(attributes, (size_t) max_attr + 1))
5fc5e2f5 1153 return -ENOMEM;
4203fc8b 1154
416e8419 1155 if (attributes[attr].offset != 0)
409856d3 1156 log_debug("sd-netlink: message parse - overwriting repeated attribute");
4203fc8b 1157
416e8419
YW
1158 attributes[attr].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
1159 attributes[attr].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
1160 attributes[attr].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
4203fc8b
TG
1161 }
1162
ae2a15bc 1163 container->attributes = TAKE_PTR(attributes);
416e8419 1164 container->max_attribute = max_attr;
4203fc8b
TG
1165
1166 return 0;
1167}
1168
817d1cd8 1169int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
d8e538ec
TG
1170 const NLType *nl_type;
1171 const NLTypeSystem *type_system;
3dd215e0 1172 void *container;
817d1cd8 1173 uint16_t type;
d8e538ec
TG
1174 size_t size;
1175 int r;
3dd215e0
TG
1176
1177 assert_return(m, -EINVAL);
409856d3 1178 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
3dd215e0 1179
98be4292
YW
1180 nl_type = type_system_get_type(
1181 m->containers[m->n_containers].type_system,
1182 type_id);
1183 if (!nl_type)
1184 return -EOPNOTSUPP;
3dd215e0 1185
817d1cd8
DH
1186 type = type_get_type(nl_type);
1187
1188 if (type == NETLINK_TYPE_NESTED) {
98be4292
YW
1189 type_system = type_system_get_type_system(
1190 m->containers[m->n_containers].type_system,
1191 type_id);
1192 if (!type_system)
1193 return -EOPNOTSUPP;
817d1cd8 1194 } else if (type == NETLINK_TYPE_UNION) {
d8e538ec 1195 const NLTypeSystemUnion *type_system_union;
d8e538ec 1196
98be4292
YW
1197 type_system_union = type_system_get_type_system_union(
1198 m->containers[m->n_containers].type_system,
1199 type_id);
1200 if (!type_system_union)
1201 return -EOPNOTSUPP;
d8e538ec 1202
b019c545
YW
1203 switch (type_system_union_get_match_type(type_system_union)) {
1204 case NL_MATCH_SIBLING: {
4af7b60d
TG
1205 const char *key;
1206
b019c545
YW
1207 r = sd_netlink_message_read_string(
1208 m,
1209 type_system_union_get_match_attribute(type_system_union),
1210 &key);
4af7b60d
TG
1211 if (r < 0)
1212 return r;
1213
98be4292 1214 type_system = type_system_union_get_type_system_by_string(
ea073c8f 1215 type_system_union,
ea073c8f 1216 key);
98be4292
YW
1217 if (!type_system)
1218 return -EOPNOTSUPP;
4af7b60d
TG
1219
1220 break;
1221 }
b019c545 1222 case NL_MATCH_PROTOCOL: {
4af7b60d
TG
1223 int family;
1224
89489ef7 1225 r = sd_rtnl_message_get_family(m, &family);
4af7b60d
TG
1226 if (r < 0)
1227 return r;
1228
98be4292 1229 type_system = type_system_union_get_type_system_by_protocol(
ea073c8f 1230 type_system_union,
ea073c8f 1231 family);
98be4292
YW
1232 if (!type_system)
1233 return -EOPNOTSUPP;
4af7b60d
TG
1234
1235 break;
1236 }
1237 default:
04499a70 1238 assert_not_reached();
4af7b60d 1239 }
d8e538ec
TG
1240 } else
1241 return -EINVAL;
1242
4c641e99 1243 r = netlink_message_read_internal(m, type_id, &container, NULL);
3dd215e0
TG
1244 if (r < 0)
1245 return r;
620fd5d4 1246
6d185cff 1247 size = (size_t) r;
313cefa1 1248 m->n_containers++;
3dd215e0 1249
f663aeb8
TG
1250 r = netlink_container_parse(m,
1251 &m->containers[m->n_containers],
f663aeb8
TG
1252 container,
1253 size);
d8e538ec 1254 if (r < 0) {
313cefa1 1255 m->n_containers--;
3dd215e0 1256 return r;
d8e538ec 1257 }
3dd215e0 1258
f663aeb8 1259 m->containers[m->n_containers].type_system = type_system;
3dd215e0
TG
1260
1261 return 0;
1262}
1263
5fc5e2f5
YW
1264int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) {
1265 void *container;
1266 size_t size;
1267 int r;
1268
1269 assert_return(m, -EINVAL);
409856d3 1270 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
5fc5e2f5
YW
1271
1272 r = netlink_message_read_internal(m, type_id, &container, NULL);
1273 if (r < 0)
1274 return r;
1275
1276 size = (size_t) r;
5fc5e2f5
YW
1277 m->n_containers++;
1278
1279 r = netlink_container_parse(m,
1280 &m->containers[m->n_containers],
1281 container,
1282 size);
1283 if (r < 0) {
1284 m->n_containers--;
1285 return r;
1286 }
1287
1288 m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
1289
1290 return 0;
1291}
1292
1c4baffc 1293int sd_netlink_message_exit_container(sd_netlink_message *m) {
e5c4350b
TG
1294 assert_return(m, -EINVAL);
1295 assert_return(m->sealed, -EINVAL);
1296 assert_return(m->n_containers > 0, -EINVAL);
1297
a1e58e8e 1298 m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes);
416e8419 1299 m->containers[m->n_containers].max_attribute = 0;
f663aeb8 1300 m->containers[m->n_containers].type_system = NULL;
3dd215e0 1301
313cefa1 1302 m->n_containers--;
e5c4350b
TG
1303
1304 return 0;
1305}
1306
416e8419
YW
1307int sd_netlink_message_get_max_attribute(sd_netlink_message *m, uint16_t *ret) {
1308 assert_return(m, -EINVAL);
1309 assert_return(m->sealed, -EINVAL);
1310 assert_return(ret, -EINVAL);
1311
1312 *ret = m->containers[m->n_containers].max_attribute;
1313 return 0;
1314}
1315
409856d3 1316uint32_t message_get_serial(sd_netlink_message *m) {
65f568bb 1317 assert(m);
9d0db178 1318 assert(m->hdr);
65f568bb
TG
1319
1320 return m->hdr->nlmsg_seq;
1321}
1322
041ea9f9 1323int sd_netlink_message_is_error(sd_netlink_message *m) {
45af44d4
TG
1324 assert_return(m, 0);
1325 assert_return(m->hdr, 0);
1326
1327 return m->hdr->nlmsg_type == NLMSG_ERROR;
1328}
1329
041ea9f9 1330int sd_netlink_message_get_errno(sd_netlink_message *m) {
65f568bb
TG
1331 struct nlmsgerr *err;
1332
e16bcf98 1333 assert_return(m, -EINVAL);
9d0db178 1334 assert_return(m->hdr, -EINVAL);
65f568bb 1335
1c4baffc 1336 if (!sd_netlink_message_is_error(m))
65f568bb
TG
1337 return 0;
1338
1339 err = NLMSG_DATA(m->hdr);
1340
1341 return err->error;
1342}
1343
e4a1e68d
YW
1344static int netlink_message_parse_error(sd_netlink_message *m) {
1345 struct nlmsgerr *err = NLMSG_DATA(m->hdr);
1346 size_t hlen = sizeof(struct nlmsgerr);
1347
1348 /* no TLVs, nothing to do here */
1349 if (!(m->hdr->nlmsg_flags & NLM_F_ACK_TLVS))
1350 return 0;
1351
1352 /* if NLM_F_CAPPED is set then the inner err msg was capped */
1353 if (!(m->hdr->nlmsg_flags & NLM_F_CAPPED))
1354 hlen += err->msg.nlmsg_len - sizeof(struct nlmsghdr);
1355
1356 if (m->hdr->nlmsg_len <= NLMSG_SPACE(hlen))
1357 return 0;
1358
1359 return netlink_container_parse(m,
1360 &m->containers[m->n_containers],
1361 (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + hlen),
1362 NLMSG_PAYLOAD(m->hdr, hlen));
1363}
1364
aee6309b 1365int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
817d1cd8 1366 size_t size;
3dd215e0 1367 int r;
0fc7531b
TG
1368
1369 assert_return(m, -EINVAL);
aee6309b 1370 assert_return(nl, -EINVAL);
0fc7531b 1371
3dd215e0 1372 /* don't allow appending to message once parsed */
c737abd3 1373 message_seal(m);
3dd215e0 1374
c7209bcf 1375 for (unsigned i = 1; i <= m->n_containers; i++)
a1e58e8e 1376 m->containers[i].attributes = mfree(m->containers[i].attributes);
3dd215e0
TG
1377
1378 m->n_containers = 0;
1379
ece174c5 1380 if (m->containers[0].attributes)
3dd215e0
TG
1381 /* top-level attributes have already been parsed */
1382 return 0;
3dd215e0 1383
d8e538ec
TG
1384 assert(m->hdr);
1385
56fdc16d
YW
1386 r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
1387 &m->containers[0].type_system, &size);
d8e538ec
TG
1388 if (r < 0)
1389 return r;
1390
56fdc16d
YW
1391 if (sd_netlink_message_is_error(m))
1392 return netlink_message_parse_error(m);
d8e538ec 1393
56fdc16d
YW
1394 return netlink_container_parse(m,
1395 &m->containers[0],
1396 (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
1397 NLMSG_PAYLOAD(m->hdr, size));
0fc7531b 1398}
3dd215e0 1399
409856d3 1400void message_seal(sd_netlink_message *m) {
3dd215e0 1401 assert(m);
3dd215e0
TG
1402
1403 m->sealed = true;
1404}
1403f45a 1405
1c4baffc 1406sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) {
1403f45a
TG
1407 assert_return(m, NULL);
1408
1409 return m->next;
1410}