]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-message.c
basic/linux: update nl80211.h
[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);
bce67bbe 119
ffeb16f5
YW
120 assert_return(IN_SET(m->hdr->nlmsg_type,
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
3f42446d 169 return m->broadcast;
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
YW
753int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
754 void *attr_data, *data;
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) {
764 data = memdup(attr_data, r);
765 if (!data)
766 return -ENOMEM;
767
768 *ret_data = data;
769 }
770
771 if (ret_size)
772 *ret_size = r;
773
774 return r;
775}
776
59d4103f
YW
777int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
778 void *attr_data;
779 char *str;
780 int r;
781
782 assert_return(m, -EINVAL);
783
784 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
785 if (r < 0)
786 return r;
787
788 r = netlink_message_read_internal(m, type, &attr_data, NULL);
789 if (r < 0)
790 return r;
791
792 if (data) {
793 str = strndup(attr_data, r);
794 if (!str)
795 return -ENOMEM;
796
797 *data = str;
798 }
799
800 return 0;
801}
802
1c4baffc 803int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
44caa5e7
SS
804 int r;
805 void *attr_data;
806
73ae2b7d
TG
807 assert_return(m, -EINVAL);
808
6c14ad61 809 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
d8e538ec
TG
810 if (r < 0)
811 return r;
44caa5e7 812
4c641e99 813 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 814 if (r < 0)
44caa5e7 815 return r;
f66eeb6b
TG
816 else if (strnlen(attr_data, r) >= (size_t) r)
817 return -EIO;
44caa5e7 818
73ae2b7d
TG
819 if (data)
820 *data = (const char *) attr_data;
44caa5e7
SS
821
822 return 0;
823}
824
1c4baffc 825int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
44caa5e7
SS
826 int r;
827 void *attr_data;
828
73ae2b7d
TG
829 assert_return(m, -EINVAL);
830
6c14ad61 831 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
d8e538ec
TG
832 if (r < 0)
833 return r;
44caa5e7 834
4c641e99 835 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 836 if (r < 0)
44caa5e7 837 return r;
f66eeb6b
TG
838 else if ((size_t) r < sizeof(uint8_t))
839 return -EIO;
44caa5e7 840
73ae2b7d
TG
841 if (data)
842 *data = *(uint8_t *) attr_data;
44caa5e7
SS
843
844 return 0;
845}
846
1c4baffc 847int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
44caa5e7 848 void *attr_data;
4c641e99
TG
849 bool net_byteorder;
850 int r;
44caa5e7 851
73ae2b7d
TG
852 assert_return(m, -EINVAL);
853
6c14ad61 854 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
d8e538ec
TG
855 if (r < 0)
856 return r;
44caa5e7 857
4c641e99 858 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 859 if (r < 0)
44caa5e7 860 return r;
f66eeb6b
TG
861 else if ((size_t) r < sizeof(uint16_t))
862 return -EIO;
44caa5e7 863
4c641e99
TG
864 if (data) {
865 if (net_byteorder)
866 *data = be16toh(*(uint16_t *) attr_data);
867 else
868 *data = *(uint16_t *) attr_data;
869 }
44caa5e7
SS
870
871 return 0;
872}
873
1c4baffc 874int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
44caa5e7 875 void *attr_data;
4c641e99
TG
876 bool net_byteorder;
877 int r;
44caa5e7 878
73ae2b7d
TG
879 assert_return(m, -EINVAL);
880
6c14ad61 881 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
d8e538ec
TG
882 if (r < 0)
883 return r;
44caa5e7 884
4c641e99 885 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 886 if (r < 0)
44caa5e7 887 return r;
6d185cff 888 else if ((size_t) r < sizeof(uint32_t))
f66eeb6b 889 return -EIO;
44caa5e7 890
4c641e99
TG
891 if (data) {
892 if (net_byteorder)
893 *data = be32toh(*(uint32_t *) attr_data);
894 else
895 *data = *(uint32_t *) attr_data;
896 }
44caa5e7
SS
897
898 return 0;
899}
900
1c4baffc 901int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
4e9e7f18
SS
902 int r;
903 void *attr_data;
904
73ae2b7d
TG
905 assert_return(m, -EINVAL);
906
6c14ad61 907 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
d8e538ec
TG
908 if (r < 0)
909 return r;
4e9e7f18 910
4c641e99 911 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 912 if (r < 0)
4e9e7f18 913 return r;
6d185cff 914 else if ((size_t) r < sizeof(struct ether_addr))
f66eeb6b 915 return -EIO;
4e9e7f18 916
73ae2b7d
TG
917 if (data)
918 memcpy(data, attr_data, sizeof(struct ether_addr));
4e9e7f18
SS
919
920 return 0;
921}
922
ca2b7cd8 923int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) {
4fc8a29a
TR
924 int r;
925 void *attr_data;
926
927 assert_return(m, -EINVAL);
928
929 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
930 if (r < 0)
931 return r;
932
933 r = netlink_message_read_internal(m, type, &attr_data, NULL);
934 if (r < 0)
935 return r;
ca2b7cd8 936 else if (r > HW_ADDR_MAX_SIZE)
4fc8a29a
TR
937 return -EIO;
938
939 if (data) {
ca2b7cd8 940 memcpy(data->bytes, attr_data, r);
4fc8a29a
TR
941 data->length = r;
942 }
943
944 return 0;
945}
946
1c4baffc 947int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
aba496a5
UTL
948 int r;
949 void *attr_data;
950
73ae2b7d
TG
951 assert_return(m, -EINVAL);
952
6c14ad61 953 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
aba496a5
UTL
954 if (r < 0)
955 return r;
956
4c641e99 957 r = netlink_message_read_internal(m, type, &attr_data, NULL);
aba496a5
UTL
958 if (r < 0)
959 return r;
6d185cff 960 else if ((size_t) r < sizeof(struct ifa_cacheinfo))
aba496a5
UTL
961 return -EIO;
962
73ae2b7d
TG
963 if (info)
964 memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
aba496a5
UTL
965
966 return 0;
967}
968
f29b6b37 969int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) {
4e9e7f18 970 void *attr_data;
f29b6b37 971 int r;
4e9e7f18 972
73ae2b7d 973 assert_return(m, -EINVAL);
f29b6b37 974 assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
73ae2b7d 975
6c14ad61 976 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
d8e538ec
TG
977 if (r < 0)
978 return r;
4e9e7f18 979
4c641e99 980 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 981 if (r < 0)
4e9e7f18 982 return r;
f29b6b37 983 else if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
f66eeb6b 984 return -EIO;
4e9e7f18 985
73ae2b7d 986 if (data)
f29b6b37 987 memcpy(data, attr_data, FAMILY_ADDRESS_SIZE(family));
4e9e7f18
SS
988
989 return 0;
990}
991
f29b6b37
YW
992int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
993 union in_addr_union u;
4e9e7f18 994 int r;
4e9e7f18 995
f29b6b37
YW
996 r = netlink_message_read_in_addr_union(m, type, AF_INET, &u);
997 if (r >= 0 && data)
998 *data = u.in;
73ae2b7d 999
f29b6b37
YW
1000 return r;
1001}
4e9e7f18 1002
f29b6b37
YW
1003int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
1004 union in_addr_union u;
1005 int r;
4e9e7f18 1006
f29b6b37
YW
1007 r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u);
1008 if (r >= 0 && data)
1009 *data = u.in6;
4e9e7f18 1010
f29b6b37 1011 return r;
4e9e7f18
SS
1012}
1013
3b4e3ebb
YW
1014int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type) {
1015 void *attr_data;
1016 int r;
1017
1018 assert_return(m, -EINVAL);
1019
1020 /* This returns 1 when the flag is set, 0 when not set, negative errno on error. */
1021
1022 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_FLAG);
1023 if (r < 0)
1024 return r;
1025
1026 r = netlink_message_read_internal(m, type, &attr_data, NULL);
1027 if (r == -ENODATA)
1028 return 0;
1029 if (r < 0)
1030 return r;
1031
1032 return 1;
1033}
1034
8f3c1859
YW
1035int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) {
1036 _cleanup_strv_free_ char **s = NULL;
1037 const NLTypeSystem *type_system;
1038 const NLType *nl_type;
1039 struct rtattr *rta;
1040 void *container;
f501c251 1041 size_t rt_len;
8f3c1859
YW
1042 int r;
1043
1044 assert_return(m, -EINVAL);
409856d3 1045 assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
8f3c1859 1046
98be4292
YW
1047 nl_type = type_system_get_type(
1048 m->containers[m->n_containers].type_system,
1049 container_type);
1050 if (!nl_type)
1051 return -EOPNOTSUPP;
8f3c1859
YW
1052
1053 if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
1054 return -EINVAL;
1055
98be4292
YW
1056 type_system = type_system_get_type_system(
1057 m->containers[m->n_containers].type_system,
1058 container_type);
1059 if (!type_system)
1060 return -EOPNOTSUPP;
8f3c1859 1061
98be4292
YW
1062 nl_type = type_system_get_type(type_system, type_id);
1063 if (!nl_type)
1064 return -EOPNOTSUPP;
8f3c1859
YW
1065
1066 if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
1067 return -EINVAL;
1068
1069 r = netlink_message_read_internal(m, container_type, &container, NULL);
1070 if (r < 0)
1071 return r;
1072
f501c251 1073 rt_len = (size_t) r;
8f3c1859
YW
1074 rta = container;
1075
f501c251
YW
1076 /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
1077 * LGTM.com analysis does not like the type difference. Hence, here we
1078 * introduce an unsigned short variable as a workaround. */
1079 unsigned short len = rt_len;
1080 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
8f3c1859
YW
1081 unsigned short type;
1082
1083 type = RTA_TYPE(rta);
1084 if (type != type_id)
1085 continue;
1086
1087 r = strv_extend(&s, RTA_DATA(rta));
1088 if (r < 0)
1089 return r;
1090 }
1091
1092 *ret = TAKE_PTR(s);
1093 return 0;
1094}
1095
416e8419
YW
1096static int netlink_container_parse(
1097 sd_netlink_message *m,
1098 struct netlink_container *container,
1099 struct rtattr *rta,
1100 size_t rt_len) {
1101
f663aeb8 1102 _cleanup_free_ struct netlink_attribute *attributes = NULL;
416e8419 1103 uint16_t max_attr = 0;
4203fc8b 1104
f501c251
YW
1105 /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
1106 * LGTM.com analysis does not like the type difference. Hence, here we
1107 * introduce an unsigned short variable as a workaround. */
1108 unsigned short len = rt_len;
1109 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
416e8419 1110 uint16_t attr;
f663aeb8 1111
416e8419
YW
1112 attr = RTA_TYPE(rta);
1113 max_attr = MAX(max_attr, attr);
4203fc8b 1114
416e8419 1115 if (!GREEDY_REALLOC0(attributes, (size_t) max_attr + 1))
5fc5e2f5 1116 return -ENOMEM;
4203fc8b 1117
416e8419 1118 if (attributes[attr].offset != 0)
409856d3 1119 log_debug("sd-netlink: message parse - overwriting repeated attribute");
4203fc8b 1120
416e8419
YW
1121 attributes[attr].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
1122 attributes[attr].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
1123 attributes[attr].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
4203fc8b
TG
1124 }
1125
ae2a15bc 1126 container->attributes = TAKE_PTR(attributes);
416e8419 1127 container->max_attribute = max_attr;
4203fc8b
TG
1128
1129 return 0;
1130}
1131
817d1cd8 1132int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
d8e538ec
TG
1133 const NLType *nl_type;
1134 const NLTypeSystem *type_system;
3dd215e0 1135 void *container;
817d1cd8 1136 uint16_t type;
d8e538ec
TG
1137 size_t size;
1138 int r;
3dd215e0
TG
1139
1140 assert_return(m, -EINVAL);
409856d3 1141 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
3dd215e0 1142
98be4292
YW
1143 nl_type = type_system_get_type(
1144 m->containers[m->n_containers].type_system,
1145 type_id);
1146 if (!nl_type)
1147 return -EOPNOTSUPP;
3dd215e0 1148
817d1cd8
DH
1149 type = type_get_type(nl_type);
1150
1151 if (type == NETLINK_TYPE_NESTED) {
98be4292
YW
1152 type_system = type_system_get_type_system(
1153 m->containers[m->n_containers].type_system,
1154 type_id);
1155 if (!type_system)
1156 return -EOPNOTSUPP;
817d1cd8 1157 } else if (type == NETLINK_TYPE_UNION) {
d8e538ec 1158 const NLTypeSystemUnion *type_system_union;
d8e538ec 1159
98be4292
YW
1160 type_system_union = type_system_get_type_system_union(
1161 m->containers[m->n_containers].type_system,
1162 type_id);
1163 if (!type_system_union)
1164 return -EOPNOTSUPP;
d8e538ec 1165
b019c545
YW
1166 switch (type_system_union_get_match_type(type_system_union)) {
1167 case NL_MATCH_SIBLING: {
4af7b60d
TG
1168 const char *key;
1169
b019c545
YW
1170 r = sd_netlink_message_read_string(
1171 m,
1172 type_system_union_get_match_attribute(type_system_union),
1173 &key);
4af7b60d
TG
1174 if (r < 0)
1175 return r;
1176
98be4292 1177 type_system = type_system_union_get_type_system_by_string(
ea073c8f 1178 type_system_union,
ea073c8f 1179 key);
98be4292
YW
1180 if (!type_system)
1181 return -EOPNOTSUPP;
4af7b60d
TG
1182
1183 break;
1184 }
b019c545 1185 case NL_MATCH_PROTOCOL: {
4af7b60d
TG
1186 int family;
1187
89489ef7 1188 r = sd_rtnl_message_get_family(m, &family);
4af7b60d
TG
1189 if (r < 0)
1190 return r;
1191
98be4292 1192 type_system = type_system_union_get_type_system_by_protocol(
ea073c8f 1193 type_system_union,
ea073c8f 1194 family);
98be4292
YW
1195 if (!type_system)
1196 return -EOPNOTSUPP;
4af7b60d
TG
1197
1198 break;
1199 }
1200 default:
04499a70 1201 assert_not_reached();
4af7b60d 1202 }
d8e538ec
TG
1203 } else
1204 return -EINVAL;
1205
4c641e99 1206 r = netlink_message_read_internal(m, type_id, &container, NULL);
3dd215e0
TG
1207 if (r < 0)
1208 return r;
620fd5d4 1209
6d185cff 1210 size = (size_t) r;
313cefa1 1211 m->n_containers++;
3dd215e0 1212
f663aeb8
TG
1213 r = netlink_container_parse(m,
1214 &m->containers[m->n_containers],
f663aeb8
TG
1215 container,
1216 size);
d8e538ec 1217 if (r < 0) {
313cefa1 1218 m->n_containers--;
3dd215e0 1219 return r;
d8e538ec 1220 }
3dd215e0 1221
f663aeb8 1222 m->containers[m->n_containers].type_system = type_system;
3dd215e0
TG
1223
1224 return 0;
1225}
1226
5fc5e2f5
YW
1227int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) {
1228 void *container;
1229 size_t size;
1230 int r;
1231
1232 assert_return(m, -EINVAL);
409856d3 1233 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
5fc5e2f5
YW
1234
1235 r = netlink_message_read_internal(m, type_id, &container, NULL);
1236 if (r < 0)
1237 return r;
1238
1239 size = (size_t) r;
5fc5e2f5
YW
1240 m->n_containers++;
1241
1242 r = netlink_container_parse(m,
1243 &m->containers[m->n_containers],
1244 container,
1245 size);
1246 if (r < 0) {
1247 m->n_containers--;
1248 return r;
1249 }
1250
1251 m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
1252
1253 return 0;
1254}
1255
1c4baffc 1256int sd_netlink_message_exit_container(sd_netlink_message *m) {
e5c4350b
TG
1257 assert_return(m, -EINVAL);
1258 assert_return(m->sealed, -EINVAL);
1259 assert_return(m->n_containers > 0, -EINVAL);
1260
a1e58e8e 1261 m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes);
416e8419 1262 m->containers[m->n_containers].max_attribute = 0;
f663aeb8 1263 m->containers[m->n_containers].type_system = NULL;
3dd215e0 1264
313cefa1 1265 m->n_containers--;
e5c4350b
TG
1266
1267 return 0;
1268}
1269
416e8419
YW
1270int sd_netlink_message_get_max_attribute(sd_netlink_message *m, uint16_t *ret) {
1271 assert_return(m, -EINVAL);
1272 assert_return(m->sealed, -EINVAL);
1273 assert_return(ret, -EINVAL);
1274
1275 *ret = m->containers[m->n_containers].max_attribute;
1276 return 0;
1277}
1278
409856d3 1279uint32_t message_get_serial(sd_netlink_message *m) {
65f568bb 1280 assert(m);
9d0db178 1281 assert(m->hdr);
65f568bb
TG
1282
1283 return m->hdr->nlmsg_seq;
1284}
1285
041ea9f9 1286int sd_netlink_message_is_error(sd_netlink_message *m) {
45af44d4
TG
1287 assert_return(m, 0);
1288 assert_return(m->hdr, 0);
1289
1290 return m->hdr->nlmsg_type == NLMSG_ERROR;
1291}
1292
041ea9f9 1293int sd_netlink_message_get_errno(sd_netlink_message *m) {
65f568bb
TG
1294 struct nlmsgerr *err;
1295
e16bcf98 1296 assert_return(m, -EINVAL);
9d0db178 1297 assert_return(m->hdr, -EINVAL);
65f568bb 1298
1c4baffc 1299 if (!sd_netlink_message_is_error(m))
65f568bb
TG
1300 return 0;
1301
1302 err = NLMSG_DATA(m->hdr);
1303
1304 return err->error;
1305}
1306
e4a1e68d
YW
1307static int netlink_message_parse_error(sd_netlink_message *m) {
1308 struct nlmsgerr *err = NLMSG_DATA(m->hdr);
1309 size_t hlen = sizeof(struct nlmsgerr);
1310
1311 /* no TLVs, nothing to do here */
1312 if (!(m->hdr->nlmsg_flags & NLM_F_ACK_TLVS))
1313 return 0;
1314
1315 /* if NLM_F_CAPPED is set then the inner err msg was capped */
1316 if (!(m->hdr->nlmsg_flags & NLM_F_CAPPED))
1317 hlen += err->msg.nlmsg_len - sizeof(struct nlmsghdr);
1318
1319 if (m->hdr->nlmsg_len <= NLMSG_SPACE(hlen))
1320 return 0;
1321
1322 return netlink_container_parse(m,
1323 &m->containers[m->n_containers],
1324 (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + hlen),
1325 NLMSG_PAYLOAD(m->hdr, hlen));
1326}
1327
aee6309b 1328int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
817d1cd8 1329 size_t size;
3dd215e0 1330 int r;
0fc7531b
TG
1331
1332 assert_return(m, -EINVAL);
aee6309b 1333 assert_return(nl, -EINVAL);
0fc7531b 1334
3dd215e0 1335 /* don't allow appending to message once parsed */
c737abd3 1336 message_seal(m);
3dd215e0 1337
c7209bcf 1338 for (unsigned i = 1; i <= m->n_containers; i++)
a1e58e8e 1339 m->containers[i].attributes = mfree(m->containers[i].attributes);
3dd215e0
TG
1340
1341 m->n_containers = 0;
1342
ece174c5 1343 if (m->containers[0].attributes)
3dd215e0
TG
1344 /* top-level attributes have already been parsed */
1345 return 0;
3dd215e0 1346
d8e538ec
TG
1347 assert(m->hdr);
1348
56fdc16d
YW
1349 r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
1350 &m->containers[0].type_system, &size);
d8e538ec
TG
1351 if (r < 0)
1352 return r;
1353
56fdc16d
YW
1354 if (sd_netlink_message_is_error(m))
1355 return netlink_message_parse_error(m);
d8e538ec 1356
56fdc16d
YW
1357 return netlink_container_parse(m,
1358 &m->containers[0],
1359 (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
1360 NLMSG_PAYLOAD(m->hdr, size));
0fc7531b 1361}
3dd215e0 1362
409856d3 1363void message_seal(sd_netlink_message *m) {
3dd215e0 1364 assert(m);
3dd215e0
TG
1365
1366 m->sealed = true;
1367}
1403f45a 1368
1c4baffc 1369sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) {
1403f45a
TG
1370 assert_return(m, NULL);
1371
1372 return m->next;
1373}