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