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