]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-message.c
sd-netlink: allow to set dump flag for genl or netfilter messages
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-message.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
65f568bb
TG
2
3#include <netinet/in.h>
65f568bb
TG
4#include <stdbool.h>
5#include <unistd.h>
6
07630cea
LP
7#include "sd-netlink.h"
8
b5efdb8a 9#include "alloc-util.h"
f97b34a6 10#include "format-util.h"
8f3c1859 11#include "memory-util.h"
1c4baffc
TG
12#include "netlink-internal.h"
13#include "netlink-types.h"
07630cea 14#include "netlink-util.h"
07630cea 15#include "socket-util.h"
8f3c1859 16#include "strv.h"
65f568bb 17
179b4db4 18#define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset))
4ebe732c 19
0a2478a9 20#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
4c641e99 21#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
0a2478a9 22
409856d3 23int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
1c4baffc 24 sd_netlink_message *m;
65f568bb 25
1cedca05
YW
26 assert(nl);
27 assert(ret);
65f568bb 28
409856d3
YW
29 /* Note that 'nl' is currently unused, if we start using it internally we must take care to
30 * avoid problems due to mutual references between buses and their queued messages. See sd-bus. */
8c578303 31
6bf8e24b 32 m = new(sd_netlink_message, 1);
65f568bb
TG
33 if (!m)
34 return -ENOMEM;
35
6bf8e24b
YW
36 *m = (sd_netlink_message) {
37 .n_ref = 1,
409856d3 38 .protocol = nl->protocol,
6bf8e24b
YW
39 .sealed = false,
40 };
65f568bb
TG
41
42 *ret = m;
65f568bb
TG
43 return 0;
44}
45
1cedca05
YW
46int message_new_full(
47 sd_netlink *nl,
48 uint16_t nlmsg_type,
49 const NLTypeSystem *type_system,
50 size_t header_size,
51 sd_netlink_message **ret) {
52
4afd3348 53 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
d8e538ec
TG
54 size_t size;
55 int r;
56
1cedca05
YW
57 assert(nl);
58 assert(type_system);
59 assert(ret);
d8e538ec 60
1cedca05
YW
61 size = NLMSG_SPACE(header_size);
62 assert(size >= sizeof(struct nlmsghdr));
12b7dff4 63
409856d3 64 r = message_new_empty(nl, &m);
d8e538ec
TG
65 if (r < 0)
66 return r;
67
1cedca05 68 m->containers[0].type_system = type_system;
1b89cf56 69
1b89cf56
TG
70 m->hdr = malloc0(size);
71 if (!m->hdr)
72 return -ENOMEM;
73
74 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
1b89cf56 75 m->hdr->nlmsg_len = size;
1cedca05 76 m->hdr->nlmsg_type = nlmsg_type;
1b89cf56 77
1cc6c93a 78 *ret = TAKE_PTR(m);
d8e538ec
TG
79 return 0;
80}
81
1cedca05 82int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
56fdc16d
YW
83 const NLTypeSystem *type_system;
84 size_t size;
1cedca05
YW
85 int r;
86
87 assert_return(nl, -EINVAL);
88 assert_return(ret, -EINVAL);
89
56fdc16d 90 r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size);
1cedca05
YW
91 if (r < 0)
92 return r;
93
56fdc16d 94 return message_new_full(nl, type, type_system, size, ret);
1cedca05
YW
95}
96
409856d3
YW
97int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
98 struct nlmsgerr *err;
99 int r;
100
101 assert(error <= 0);
102
103 r = message_new(nl, ret, NLMSG_ERROR);
104 if (r < 0)
105 return r;
106
107 message_seal(*ret);
108 (*ret)->hdr->nlmsg_seq = serial;
109
110 err = NLMSG_DATA((*ret)->hdr);
111 err->error = error;
112
113 return 0;
114}
115
1c4baffc 116int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
6e20c8f8
TG
117 assert_return(m, -EINVAL);
118 assert_return(m->hdr, -EINVAL);
ec44d3f4
YW
119 assert_return(m->protocol != NETLINK_ROUTE ||
120 IN_SET(m->hdr->nlmsg_type,
ffeb16f5 121 RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
c16c7808 122 RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL);
6e20c8f8 123
5883ff60 124 SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);
6e20c8f8
TG
125
126 return 0;
127}
128
f23ab4dc 129DEFINE_TRIVIAL_REF_FUNC(sd_netlink_message, sd_netlink_message);
65f568bb 130
1c4baffc 131sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) {
f23ab4dc 132 while (m && --m->n_ref == 0) {
3dd215e0
TG
133 unsigned i;
134
65f568bb 135 free(m->hdr);
3dd215e0 136
9f5bbfe3 137 for (i = 0; i <= m->n_containers; i++)
f663aeb8 138 free(m->containers[i].attributes);
3dd215e0 139
f23ab4dc 140 sd_netlink_message *t = m;
82e4eda6
DH
141 m = m->next;
142 free(t);
65f568bb
TG
143 }
144
145 return NULL;
146}
147
041ea9f9 148int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
65f568bb
TG
149 assert_return(m, -EINVAL);
150 assert_return(type, -EINVAL);
151
152 *type = m->hdr->nlmsg_type;
153
154 return 0;
155}
156
c06cb593
SS
157int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) {
158 assert_return(m, -EINVAL);
159 assert_return(flags, -EINVAL);
160
161 m->hdr->nlmsg_flags = flags;
162
163 return 0;
164}
165
041ea9f9 166int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
1f0db3ed
TG
167 assert_return(m, -EINVAL);
168
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 753int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
52888279 754 void *attr_data;
de52a83c
YW
755 int r;
756
757 assert_return(m, -EINVAL);
758
759 r = netlink_message_read_internal(m, type, &attr_data, NULL);
760 if (r < 0)
761 return r;
762
763 if (ret_data) {
52888279
YW
764 void *data;
765
de52a83c
YW
766 data = memdup(attr_data, r);
767 if (!data)
768 return -ENOMEM;
769
770 *ret_data = data;
771 }
772
773 if (ret_size)
774 *ret_size = r;
775
776 return r;
777}
778
59d4103f
YW
779int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
780 void *attr_data;
59d4103f
YW
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) {
52888279
YW
794 char *str;
795
59d4103f
YW
796 str = strndup(attr_data, r);
797 if (!str)
798 return -ENOMEM;
799
800 *data = str;
801 }
802
803 return 0;
804}
805
1c4baffc 806int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
44caa5e7 807 void *attr_data;
52888279 808 int r;
44caa5e7 809
73ae2b7d
TG
810 assert_return(m, -EINVAL);
811
6c14ad61 812 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
d8e538ec
TG
813 if (r < 0)
814 return r;
44caa5e7 815
4c641e99 816 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 817 if (r < 0)
44caa5e7 818 return r;
52888279
YW
819
820 if (strnlen(attr_data, r) >= (size_t) r)
f66eeb6b 821 return -EIO;
44caa5e7 822
73ae2b7d
TG
823 if (data)
824 *data = (const char *) attr_data;
44caa5e7
SS
825
826 return 0;
827}
828
1c4baffc 829int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
44caa5e7 830 void *attr_data;
52888279 831 int r;
44caa5e7 832
73ae2b7d
TG
833 assert_return(m, -EINVAL);
834
6c14ad61 835 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
d8e538ec
TG
836 if (r < 0)
837 return r;
44caa5e7 838
4c641e99 839 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 840 if (r < 0)
44caa5e7 841 return r;
52888279
YW
842
843 if ((size_t) r < sizeof(uint8_t))
f66eeb6b 844 return -EIO;
44caa5e7 845
73ae2b7d
TG
846 if (data)
847 *data = *(uint8_t *) attr_data;
44caa5e7
SS
848
849 return 0;
850}
851
1c4baffc 852int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
44caa5e7 853 void *attr_data;
4c641e99
TG
854 bool net_byteorder;
855 int r;
44caa5e7 856
73ae2b7d
TG
857 assert_return(m, -EINVAL);
858
6c14ad61 859 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
d8e538ec
TG
860 if (r < 0)
861 return r;
44caa5e7 862
4c641e99 863 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 864 if (r < 0)
44caa5e7 865 return r;
52888279
YW
866
867 if ((size_t) r < sizeof(uint16_t))
f66eeb6b 868 return -EIO;
44caa5e7 869
4c641e99
TG
870 if (data) {
871 if (net_byteorder)
872 *data = be16toh(*(uint16_t *) attr_data);
873 else
874 *data = *(uint16_t *) attr_data;
875 }
44caa5e7
SS
876
877 return 0;
878}
879
1c4baffc 880int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
44caa5e7 881 void *attr_data;
4c641e99
TG
882 bool net_byteorder;
883 int r;
44caa5e7 884
73ae2b7d
TG
885 assert_return(m, -EINVAL);
886
6c14ad61 887 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
d8e538ec
TG
888 if (r < 0)
889 return r;
44caa5e7 890
4c641e99 891 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 892 if (r < 0)
44caa5e7 893 return r;
52888279
YW
894
895 if ((size_t) r < sizeof(uint32_t))
f66eeb6b 896 return -EIO;
44caa5e7 897
4c641e99
TG
898 if (data) {
899 if (net_byteorder)
900 *data = be32toh(*(uint32_t *) attr_data);
901 else
902 *data = *(uint32_t *) attr_data;
903 }
44caa5e7
SS
904
905 return 0;
906}
907
1c4baffc 908int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
4e9e7f18 909 void *attr_data;
52888279 910 int r;
4e9e7f18 911
73ae2b7d
TG
912 assert_return(m, -EINVAL);
913
6c14ad61 914 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
d8e538ec
TG
915 if (r < 0)
916 return r;
4e9e7f18 917
4c641e99 918 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 919 if (r < 0)
4e9e7f18 920 return r;
52888279
YW
921
922 if ((size_t) r < sizeof(struct ether_addr))
f66eeb6b 923 return -EIO;
4e9e7f18 924
73ae2b7d
TG
925 if (data)
926 memcpy(data, attr_data, sizeof(struct ether_addr));
4e9e7f18
SS
927
928 return 0;
929}
930
ca2b7cd8 931int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) {
4fc8a29a 932 void *attr_data;
52888279 933 int r;
4fc8a29a
TR
934
935 assert_return(m, -EINVAL);
936
937 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
938 if (r < 0)
939 return r;
940
941 r = netlink_message_read_internal(m, type, &attr_data, NULL);
942 if (r < 0)
943 return r;
52888279
YW
944
945 if (r > HW_ADDR_MAX_SIZE)
4fc8a29a
TR
946 return -EIO;
947
948 if (data) {
ca2b7cd8 949 memcpy(data->bytes, attr_data, r);
4fc8a29a
TR
950 data->length = r;
951 }
952
953 return 0;
954}
955
1c4baffc 956int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
aba496a5 957 void *attr_data;
52888279 958 int r;
aba496a5 959
73ae2b7d
TG
960 assert_return(m, -EINVAL);
961
6c14ad61 962 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
aba496a5
UTL
963 if (r < 0)
964 return r;
965
4c641e99 966 r = netlink_message_read_internal(m, type, &attr_data, NULL);
aba496a5
UTL
967 if (r < 0)
968 return r;
52888279
YW
969
970 if ((size_t) r < sizeof(struct ifa_cacheinfo))
aba496a5
UTL
971 return -EIO;
972
73ae2b7d
TG
973 if (info)
974 memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
aba496a5
UTL
975
976 return 0;
977}
978
f29b6b37 979int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) {
4e9e7f18 980 void *attr_data;
f29b6b37 981 int r;
4e9e7f18 982
73ae2b7d 983 assert_return(m, -EINVAL);
f29b6b37 984 assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
73ae2b7d 985
6c14ad61 986 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
d8e538ec
TG
987 if (r < 0)
988 return r;
4e9e7f18 989
4c641e99 990 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 991 if (r < 0)
4e9e7f18 992 return r;
52888279
YW
993
994 if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
f66eeb6b 995 return -EIO;
4e9e7f18 996
73ae2b7d 997 if (data)
f29b6b37 998 memcpy(data, attr_data, FAMILY_ADDRESS_SIZE(family));
4e9e7f18
SS
999
1000 return 0;
1001}
1002
f29b6b37
YW
1003int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
1004 union in_addr_union u;
4e9e7f18 1005 int r;
4e9e7f18 1006
f29b6b37
YW
1007 r = netlink_message_read_in_addr_union(m, type, AF_INET, &u);
1008 if (r >= 0 && data)
1009 *data = u.in;
73ae2b7d 1010
f29b6b37
YW
1011 return r;
1012}
4e9e7f18 1013
f29b6b37
YW
1014int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
1015 union in_addr_union u;
1016 int r;
4e9e7f18 1017
f29b6b37
YW
1018 r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u);
1019 if (r >= 0 && data)
1020 *data = u.in6;
4e9e7f18 1021
f29b6b37 1022 return r;
4e9e7f18
SS
1023}
1024
3b4e3ebb
YW
1025int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type) {
1026 void *attr_data;
1027 int r;
1028
1029 assert_return(m, -EINVAL);
1030
1031 /* This returns 1 when the flag is set, 0 when not set, negative errno on error. */
1032
1033 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_FLAG);
1034 if (r < 0)
1035 return r;
1036
1037 r = netlink_message_read_internal(m, type, &attr_data, NULL);
1038 if (r == -ENODATA)
1039 return 0;
1040 if (r < 0)
1041 return r;
1042
1043 return 1;
1044}
1045
8f3c1859
YW
1046int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) {
1047 _cleanup_strv_free_ char **s = NULL;
1048 const NLTypeSystem *type_system;
1049 const NLType *nl_type;
1050 struct rtattr *rta;
1051 void *container;
f501c251 1052 size_t rt_len;
8f3c1859
YW
1053 int r;
1054
1055 assert_return(m, -EINVAL);
409856d3 1056 assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
8f3c1859 1057
98be4292
YW
1058 nl_type = type_system_get_type(
1059 m->containers[m->n_containers].type_system,
1060 container_type);
1061 if (!nl_type)
1062 return -EOPNOTSUPP;
8f3c1859
YW
1063
1064 if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
1065 return -EINVAL;
1066
98be4292
YW
1067 type_system = type_system_get_type_system(
1068 m->containers[m->n_containers].type_system,
1069 container_type);
1070 if (!type_system)
1071 return -EOPNOTSUPP;
8f3c1859 1072
98be4292
YW
1073 nl_type = type_system_get_type(type_system, type_id);
1074 if (!nl_type)
1075 return -EOPNOTSUPP;
8f3c1859
YW
1076
1077 if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
1078 return -EINVAL;
1079
1080 r = netlink_message_read_internal(m, container_type, &container, NULL);
1081 if (r < 0)
1082 return r;
1083
f501c251 1084 rt_len = (size_t) r;
8f3c1859
YW
1085 rta = container;
1086
f501c251
YW
1087 /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
1088 * LGTM.com analysis does not like the type difference. Hence, here we
1089 * introduce an unsigned short variable as a workaround. */
1090 unsigned short len = rt_len;
1091 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
8f3c1859
YW
1092 unsigned short type;
1093
1094 type = RTA_TYPE(rta);
1095 if (type != type_id)
1096 continue;
1097
1098 r = strv_extend(&s, RTA_DATA(rta));
1099 if (r < 0)
1100 return r;
1101 }
1102
1103 *ret = TAKE_PTR(s);
1104 return 0;
1105}
1106
416e8419
YW
1107static int netlink_container_parse(
1108 sd_netlink_message *m,
1109 struct netlink_container *container,
1110 struct rtattr *rta,
1111 size_t rt_len) {
1112
f663aeb8 1113 _cleanup_free_ struct netlink_attribute *attributes = NULL;
416e8419 1114 uint16_t max_attr = 0;
4203fc8b 1115
f501c251
YW
1116 /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and
1117 * LGTM.com analysis does not like the type difference. Hence, here we
1118 * introduce an unsigned short variable as a workaround. */
1119 unsigned short len = rt_len;
1120 for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
416e8419 1121 uint16_t attr;
f663aeb8 1122
416e8419
YW
1123 attr = RTA_TYPE(rta);
1124 max_attr = MAX(max_attr, attr);
4203fc8b 1125
416e8419 1126 if (!GREEDY_REALLOC0(attributes, (size_t) max_attr + 1))
5fc5e2f5 1127 return -ENOMEM;
4203fc8b 1128
416e8419 1129 if (attributes[attr].offset != 0)
409856d3 1130 log_debug("sd-netlink: message parse - overwriting repeated attribute");
4203fc8b 1131
416e8419
YW
1132 attributes[attr].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
1133 attributes[attr].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
1134 attributes[attr].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
4203fc8b
TG
1135 }
1136
ae2a15bc 1137 container->attributes = TAKE_PTR(attributes);
416e8419 1138 container->max_attribute = max_attr;
4203fc8b
TG
1139
1140 return 0;
1141}
1142
817d1cd8 1143int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
d8e538ec
TG
1144 const NLType *nl_type;
1145 const NLTypeSystem *type_system;
3dd215e0 1146 void *container;
817d1cd8 1147 uint16_t type;
d8e538ec
TG
1148 size_t size;
1149 int r;
3dd215e0
TG
1150
1151 assert_return(m, -EINVAL);
409856d3 1152 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
3dd215e0 1153
98be4292
YW
1154 nl_type = type_system_get_type(
1155 m->containers[m->n_containers].type_system,
1156 type_id);
1157 if (!nl_type)
1158 return -EOPNOTSUPP;
3dd215e0 1159
817d1cd8
DH
1160 type = type_get_type(nl_type);
1161
1162 if (type == NETLINK_TYPE_NESTED) {
98be4292
YW
1163 type_system = type_system_get_type_system(
1164 m->containers[m->n_containers].type_system,
1165 type_id);
1166 if (!type_system)
1167 return -EOPNOTSUPP;
817d1cd8 1168 } else if (type == NETLINK_TYPE_UNION) {
d8e538ec 1169 const NLTypeSystemUnion *type_system_union;
d8e538ec 1170
98be4292
YW
1171 type_system_union = type_system_get_type_system_union(
1172 m->containers[m->n_containers].type_system,
1173 type_id);
1174 if (!type_system_union)
1175 return -EOPNOTSUPP;
d8e538ec 1176
b019c545
YW
1177 switch (type_system_union_get_match_type(type_system_union)) {
1178 case NL_MATCH_SIBLING: {
4af7b60d
TG
1179 const char *key;
1180
b019c545
YW
1181 r = sd_netlink_message_read_string(
1182 m,
1183 type_system_union_get_match_attribute(type_system_union),
1184 &key);
4af7b60d
TG
1185 if (r < 0)
1186 return r;
1187
98be4292 1188 type_system = type_system_union_get_type_system_by_string(
ea073c8f 1189 type_system_union,
ea073c8f 1190 key);
98be4292
YW
1191 if (!type_system)
1192 return -EOPNOTSUPP;
4af7b60d
TG
1193
1194 break;
1195 }
b019c545 1196 case NL_MATCH_PROTOCOL: {
4af7b60d
TG
1197 int family;
1198
89489ef7 1199 r = sd_rtnl_message_get_family(m, &family);
4af7b60d
TG
1200 if (r < 0)
1201 return r;
1202
98be4292 1203 type_system = type_system_union_get_type_system_by_protocol(
ea073c8f 1204 type_system_union,
ea073c8f 1205 family);
98be4292
YW
1206 if (!type_system)
1207 return -EOPNOTSUPP;
4af7b60d
TG
1208
1209 break;
1210 }
1211 default:
04499a70 1212 assert_not_reached();
4af7b60d 1213 }
d8e538ec
TG
1214 } else
1215 return -EINVAL;
1216
4c641e99 1217 r = netlink_message_read_internal(m, type_id, &container, NULL);
3dd215e0
TG
1218 if (r < 0)
1219 return r;
620fd5d4 1220
6d185cff 1221 size = (size_t) r;
313cefa1 1222 m->n_containers++;
3dd215e0 1223
f663aeb8
TG
1224 r = netlink_container_parse(m,
1225 &m->containers[m->n_containers],
f663aeb8
TG
1226 container,
1227 size);
d8e538ec 1228 if (r < 0) {
313cefa1 1229 m->n_containers--;
3dd215e0 1230 return r;
d8e538ec 1231 }
3dd215e0 1232
f663aeb8 1233 m->containers[m->n_containers].type_system = type_system;
3dd215e0
TG
1234
1235 return 0;
1236}
1237
5fc5e2f5
YW
1238int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) {
1239 void *container;
1240 size_t size;
1241 int r;
1242
1243 assert_return(m, -EINVAL);
409856d3 1244 assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
5fc5e2f5
YW
1245
1246 r = netlink_message_read_internal(m, type_id, &container, NULL);
1247 if (r < 0)
1248 return r;
1249
1250 size = (size_t) r;
5fc5e2f5
YW
1251 m->n_containers++;
1252
1253 r = netlink_container_parse(m,
1254 &m->containers[m->n_containers],
1255 container,
1256 size);
1257 if (r < 0) {
1258 m->n_containers--;
1259 return r;
1260 }
1261
1262 m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
1263
1264 return 0;
1265}
1266
1c4baffc 1267int sd_netlink_message_exit_container(sd_netlink_message *m) {
e5c4350b
TG
1268 assert_return(m, -EINVAL);
1269 assert_return(m->sealed, -EINVAL);
1270 assert_return(m->n_containers > 0, -EINVAL);
1271
a1e58e8e 1272 m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes);
416e8419 1273 m->containers[m->n_containers].max_attribute = 0;
f663aeb8 1274 m->containers[m->n_containers].type_system = NULL;
3dd215e0 1275
313cefa1 1276 m->n_containers--;
e5c4350b
TG
1277
1278 return 0;
1279}
1280
416e8419
YW
1281int sd_netlink_message_get_max_attribute(sd_netlink_message *m, uint16_t *ret) {
1282 assert_return(m, -EINVAL);
1283 assert_return(m->sealed, -EINVAL);
1284 assert_return(ret, -EINVAL);
1285
1286 *ret = m->containers[m->n_containers].max_attribute;
1287 return 0;
1288}
1289
409856d3 1290uint32_t message_get_serial(sd_netlink_message *m) {
65f568bb 1291 assert(m);
9d0db178 1292 assert(m->hdr);
65f568bb
TG
1293
1294 return m->hdr->nlmsg_seq;
1295}
1296
041ea9f9 1297int sd_netlink_message_is_error(sd_netlink_message *m) {
45af44d4
TG
1298 assert_return(m, 0);
1299 assert_return(m->hdr, 0);
1300
1301 return m->hdr->nlmsg_type == NLMSG_ERROR;
1302}
1303
041ea9f9 1304int sd_netlink_message_get_errno(sd_netlink_message *m) {
65f568bb
TG
1305 struct nlmsgerr *err;
1306
e16bcf98 1307 assert_return(m, -EINVAL);
9d0db178 1308 assert_return(m->hdr, -EINVAL);
65f568bb 1309
1c4baffc 1310 if (!sd_netlink_message_is_error(m))
65f568bb
TG
1311 return 0;
1312
1313 err = NLMSG_DATA(m->hdr);
1314
1315 return err->error;
1316}
1317
e4a1e68d
YW
1318static int netlink_message_parse_error(sd_netlink_message *m) {
1319 struct nlmsgerr *err = NLMSG_DATA(m->hdr);
1320 size_t hlen = sizeof(struct nlmsgerr);
1321
1322 /* no TLVs, nothing to do here */
1323 if (!(m->hdr->nlmsg_flags & NLM_F_ACK_TLVS))
1324 return 0;
1325
1326 /* if NLM_F_CAPPED is set then the inner err msg was capped */
1327 if (!(m->hdr->nlmsg_flags & NLM_F_CAPPED))
1328 hlen += err->msg.nlmsg_len - sizeof(struct nlmsghdr);
1329
1330 if (m->hdr->nlmsg_len <= NLMSG_SPACE(hlen))
1331 return 0;
1332
1333 return netlink_container_parse(m,
1334 &m->containers[m->n_containers],
1335 (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + hlen),
1336 NLMSG_PAYLOAD(m->hdr, hlen));
1337}
1338
aee6309b 1339int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
817d1cd8 1340 size_t size;
3dd215e0 1341 int r;
0fc7531b
TG
1342
1343 assert_return(m, -EINVAL);
aee6309b 1344 assert_return(nl, -EINVAL);
0fc7531b 1345
3dd215e0 1346 /* don't allow appending to message once parsed */
c737abd3 1347 message_seal(m);
3dd215e0 1348
c7209bcf 1349 for (unsigned i = 1; i <= m->n_containers; i++)
a1e58e8e 1350 m->containers[i].attributes = mfree(m->containers[i].attributes);
3dd215e0
TG
1351
1352 m->n_containers = 0;
1353
ece174c5 1354 if (m->containers[0].attributes)
3dd215e0
TG
1355 /* top-level attributes have already been parsed */
1356 return 0;
3dd215e0 1357
d8e538ec
TG
1358 assert(m->hdr);
1359
56fdc16d
YW
1360 r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
1361 &m->containers[0].type_system, &size);
d8e538ec
TG
1362 if (r < 0)
1363 return r;
1364
56fdc16d
YW
1365 if (sd_netlink_message_is_error(m))
1366 return netlink_message_parse_error(m);
d8e538ec 1367
56fdc16d
YW
1368 return netlink_container_parse(m,
1369 &m->containers[0],
1370 (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
1371 NLMSG_PAYLOAD(m->hdr, size));
0fc7531b 1372}
3dd215e0 1373
409856d3 1374void message_seal(sd_netlink_message *m) {
3dd215e0 1375 assert(m);
3dd215e0
TG
1376
1377 m->sealed = true;
1378}
1403f45a 1379
1c4baffc 1380sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) {
1403f45a
TG
1381 assert_return(m, NULL);
1382
1383 return m->next;
1384}