]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-message.c
tree-wide: beautify remaining copyright statements
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-message.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
65f568bb 2/***
96b2fb93 3 Copyright © 2013 Tom Gundersen <teg@jklm.no>
65f568bb
TG
4***/
5
6#include <netinet/in.h>
65f568bb
TG
7#include <stdbool.h>
8#include <unistd.h>
9
07630cea
LP
10#include "sd-netlink.h"
11
b5efdb8a 12#include "alloc-util.h"
f97b34a6 13#include "format-util.h"
d5eff740 14#include "missing.h"
1c4baffc
TG
15#include "netlink-internal.h"
16#include "netlink-types.h"
07630cea
LP
17#include "netlink-util.h"
18#include "refcnt.h"
19#include "socket-util.h"
20#include "util.h"
65f568bb 21
f663aeb8 22#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL)
313cefa1 23#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
4ebe732c 24
0a2478a9 25#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
4c641e99 26#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
0a2478a9 27
89489ef7 28int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
1c4baffc 29 sd_netlink_message *m;
65f568bb
TG
30
31 assert_return(ret, -EINVAL);
65f568bb 32
f131770b 33 /* Note that 'rtnl' is currently unused, if we start using it internally
8c578303 34 we must take care to avoid problems due to mutual references between
ff9b60f3 35 buses and their queued messages. See sd-bus.
8c578303
TG
36 */
37
1c4baffc 38 m = new0(sd_netlink_message, 1);
65f568bb
TG
39 if (!m)
40 return -ENOMEM;
41
65f568bb 42 m->n_ref = REFCNT_INIT;
05d0c2e3 43 m->protocol = rtnl->protocol;
65f568bb
TG
44 m->sealed = false;
45
46 *ret = m;
47
48 return 0;
49}
50
1c4baffc 51int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
4afd3348 52 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
d8e538ec 53 const NLType *nl_type;
05d0c2e3 54 const NLTypeSystem *type_system_root;
d8e538ec
TG
55 size_t size;
56 int r;
57
05d0c2e3
JT
58 assert_return(rtnl, -EINVAL);
59
60 type_system_root = type_system_get_root(rtnl->protocol);
61
62 r = type_system_get_type(type_system_root, &nl_type, type);
d8e538ec
TG
63 if (r < 0)
64 return r;
65
12b7dff4
DH
66 if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
67 return -EINVAL;
68
1b89cf56 69 r = message_new_empty(rtnl, &m);
d8e538ec
TG
70 if (r < 0)
71 return r;
72
817d1cd8 73 size = NLMSG_SPACE(type_get_size(nl_type));
1b89cf56
TG
74
75 assert(size >= sizeof(struct nlmsghdr));
76 m->hdr = malloc0(size);
77 if (!m->hdr)
78 return -ENOMEM;
79
80 m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
81
f663aeb8 82 type_get_type_system(nl_type, &m->containers[0].type_system);
1b89cf56
TG
83 m->hdr->nlmsg_len = size;
84 m->hdr->nlmsg_type = type;
85
1cc6c93a 86 *ret = TAKE_PTR(m);
d8e538ec
TG
87
88 return 0;
89}
90
1c4baffc 91int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
6e20c8f8
TG
92 assert_return(m, -EINVAL);
93 assert_return(m->hdr, -EINVAL);
bce67bbe
SS
94
95 assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL), -EINVAL);
6e20c8f8 96
5883ff60 97 SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);
6e20c8f8
TG
98
99 return 0;
100}
101
1c4baffc 102sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) {
f1dd72c2
LP
103 if (!m)
104 return NULL;
65f568bb 105
f1dd72c2 106 assert_se(REFCNT_INC(m->n_ref) >= 2);
65f568bb
TG
107 return m;
108}
109
1c4baffc 110sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) {
82e4eda6
DH
111 sd_netlink_message *t;
112
113 while (m && REFCNT_DEC(m->n_ref) == 0) {
3dd215e0
TG
114 unsigned i;
115
65f568bb 116 free(m->hdr);
3dd215e0 117
9f5bbfe3 118 for (i = 0; i <= m->n_containers; i++)
f663aeb8 119 free(m->containers[i].attributes);
3dd215e0 120
82e4eda6
DH
121 t = m;
122 m = m->next;
123 free(t);
65f568bb
TG
124 }
125
126 return NULL;
127}
128
1c4baffc 129int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
65f568bb
TG
130 assert_return(m, -EINVAL);
131 assert_return(type, -EINVAL);
132
133 *type = m->hdr->nlmsg_type;
134
135 return 0;
136}
137
c06cb593
SS
138int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) {
139 assert_return(m, -EINVAL);
140 assert_return(flags, -EINVAL);
141
142 m->hdr->nlmsg_flags = flags;
143
144 return 0;
145}
146
1c4baffc 147int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
1f0db3ed
TG
148 assert_return(m, -EINVAL);
149
3f42446d 150 return m->broadcast;
1f0db3ed
TG
151}
152
4ebe732c
ZJS
153/* If successful the updated message will be correctly aligned, if
154 unsuccessful the old message is untouched. */
1c4baffc 155static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) {
7ca1d319
TG
156 uint32_t rta_length;
157 size_t message_length, padding_length;
65f568bb
TG
158 struct nlmsghdr *new_hdr;
159 struct rtattr *rta;
8e337e64 160 char *padding;
5a081409 161 unsigned i;
7ca1d319 162 int offset;
65f568bb 163
33125ac5
TG
164 assert(m);
165 assert(m->hdr);
e5c4350b 166 assert(!m->sealed);
33125ac5 167 assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
7ca1d319
TG
168 assert(!data || data_length);
169
170 /* get offset of the new attribute */
171 offset = m->hdr->nlmsg_len;
65f568bb 172
8e337e64 173 /* get the size of the new rta attribute (with padding at the end) */
65f568bb 174 rta_length = RTA_LENGTH(data_length);
4ebe732c
ZJS
175
176 /* get the new message size (with padding at the end) */
7ca1d319 177 message_length = offset + RTA_ALIGN(rta_length);
65f568bb 178
05d0c2e3
JT
179 /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
180 if (message_length > MIN(page_size(), 8192UL))
181 return -ENOBUFS;
182
65f568bb
TG
183 /* realloc to fit the new attribute */
184 new_hdr = realloc(m->hdr, message_length);
185 if (!new_hdr)
186 return -ENOMEM;
187 m->hdr = new_hdr;
188
189 /* get pointer to the attribute we are about to add */
7ca1d319 190 rta = (struct rtattr *) ((uint8_t *) m->hdr + offset);
65f568bb 191
5a081409
TG
192 /* if we are inside containers, extend them */
193 for (i = 0; i < m->n_containers; i++)
7ca1d319 194 GET_CONTAINER(m, i)->rta_len += message_length - offset;
33125ac5 195
65f568bb
TG
196 /* fill in the attribute */
197 rta->rta_type = type;
198 rta->rta_len = rta_length;
7ca1d319 199 if (data)
33125ac5
TG
200 /* we don't deal with the case where the user lies about the type
201 * and gives us too little data (so don't do that)
7ca1d319 202 */
33125ac5 203 padding = mempcpy(RTA_DATA(rta), data, data_length);
b8cc01a2
SS
204
205 else
7ca1d319
TG
206 /* if no data was passed, make sure we still initialize the padding
207 note that we can have data_length > 0 (used by some containers) */
208 padding = RTA_DATA(rta);
65f568bb 209
7ca1d319
TG
210 /* make sure also the padding at the end of the message is initialized */
211 padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding;
212 memzero(padding, padding_length);
213
4ebe732c
ZJS
214 /* update message size */
215 m->hdr->nlmsg_len = message_length;
216
7ca1d319 217 return offset;
65f568bb
TG
218}
219
6c14ad61 220static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) {
d8e538ec
TG
221 const NLType *type;
222 int r;
223
6c14ad61
DH
224 assert(m);
225
f663aeb8 226 r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type);
d8e538ec
TG
227 if (r < 0)
228 return r;
229
817d1cd8 230 if (type_get_type(type) != data_type)
d8e538ec
TG
231 return -EINVAL;
232
6c14ad61
DH
233 if (out_size)
234 *out_size = type_get_size(type);
235 return 0;
d8e538ec
TG
236}
237
1c4baffc 238int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) {
d8e538ec 239 size_t length, size;
0a0dc69b 240 int r;
65f568bb
TG
241
242 assert_return(m, -EINVAL);
e5c4350b 243 assert_return(!m->sealed, -EPERM);
65f568bb
TG
244 assert_return(data, -EINVAL);
245
6c14ad61 246 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
0a0dc69b
TG
247 if (r < 0)
248 return r;
65f568bb 249
d8e538ec 250 if (size) {
3072eecf
LP
251 length = strnlen(data, size+1);
252 if (length > size)
d8e538ec
TG
253 return -EINVAL;
254 } else
255 length = strlen(data);
33125ac5 256
d8e538ec 257 r = add_rtattr(m, type, data, length + 1);
0a0dc69b
TG
258 if (r < 0)
259 return r;
260
261 return 0;
262}
263
c06aead0
SS
264int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) {
265 size_t size;
266 int r;
267
268 assert_return(m, -EINVAL);
269 assert_return(!m->sealed, -EPERM);
270
271 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG);
272 if (r < 0)
273 return r;
274
275 r = add_rtattr(m, type, NULL, 0);
276 if (r < 0)
277 return r;
278
279 return 0;
280}
281
1c4baffc 282int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) {
7b179640
SS
283 int r;
284
285 assert_return(m, -EINVAL);
286 assert_return(!m->sealed, -EPERM);
287
6c14ad61 288 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
7b179640
SS
289 if (r < 0)
290 return r;
291
7b179640
SS
292 r = add_rtattr(m, type, &data, sizeof(uint8_t));
293 if (r < 0)
294 return r;
295
296 return 0;
297}
298
1c4baffc 299int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) {
01b36069
TG
300 int r;
301
302 assert_return(m, -EINVAL);
e5c4350b 303 assert_return(!m->sealed, -EPERM);
01b36069 304
6c14ad61 305 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
01b36069
TG
306 if (r < 0)
307 return r;
308
01b36069
TG
309 r = add_rtattr(m, type, &data, sizeof(uint16_t));
310 if (r < 0)
311 return r;
312
313 return 0;
314}
315
1c4baffc 316int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) {
0a0dc69b
TG
317 int r;
318
319 assert_return(m, -EINVAL);
e5c4350b 320 assert_return(!m->sealed, -EPERM);
0a0dc69b 321
6c14ad61 322 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
0a0dc69b
TG
323 if (r < 0)
324 return r;
325
4d47756b 326 r = add_rtattr(m, type, &data, sizeof(uint32_t));
0a0dc69b
TG
327 if (r < 0)
328 return r;
329
330 return 0;
331}
332
17af840b
SS
333int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
334 int r;
335
336 assert_return(m, -EINVAL);
337 assert_return(!m->sealed, -EPERM);
338
b8cc01a2 339 r = add_rtattr(m, type, data, len);
17af840b
SS
340 if (r < 0)
341 return r;
342
343 return 0;
344}
345
1c4baffc 346int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) {
0a0dc69b
TG
347 int r;
348
349 assert_return(m, -EINVAL);
e5c4350b 350 assert_return(!m->sealed, -EPERM);
0a0dc69b
TG
351 assert_return(data, -EINVAL);
352
6c14ad61 353 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
0a0dc69b
TG
354 if (r < 0)
355 return r;
356
4d47756b 357 r = add_rtattr(m, type, data, sizeof(struct in_addr));
0a0dc69b
TG
358 if (r < 0)
359 return r;
360
361 return 0;
362}
363
1c4baffc 364int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) {
0a0dc69b
TG
365 int r;
366
367 assert_return(m, -EINVAL);
e5c4350b 368 assert_return(!m->sealed, -EPERM);
0a0dc69b
TG
369 assert_return(data, -EINVAL);
370
6c14ad61 371 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
0a0dc69b
TG
372 if (r < 0)
373 return r;
374
4d47756b 375 r = add_rtattr(m, type, data, sizeof(struct in6_addr));
0a0dc69b
TG
376 if (r < 0)
377 return r;
378
379 return 0;
380}
381
1c4baffc 382int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) {
0a0dc69b
TG
383 int r;
384
385 assert_return(m, -EINVAL);
e5c4350b 386 assert_return(!m->sealed, -EPERM);
0a0dc69b
TG
387 assert_return(data, -EINVAL);
388
6c14ad61 389 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
d8e538ec
TG
390 if (r < 0)
391 return r;
0a0dc69b 392
b9eaf3d1 393 r = add_rtattr(m, type, data, ETH_ALEN);
0a0dc69b
TG
394 if (r < 0)
395 return r;
396
397 return 0;
65f568bb
TG
398}
399
1c4baffc 400int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) {
aba496a5
UTL
401 int r;
402
403 assert_return(m, -EINVAL);
404 assert_return(!m->sealed, -EPERM);
405 assert_return(info, -EINVAL);
406
6c14ad61 407 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
aba496a5
UTL
408 if (r < 0)
409 return r;
410
411 r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo));
412 if (r < 0)
413 return r;
414
415 return 0;
416}
417
1c4baffc 418int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) {
d8e538ec
TG
419 size_t size;
420 int r;
33125ac5 421
65f568bb 422 assert_return(m, -EINVAL);
e5c4350b 423 assert_return(!m->sealed, -EPERM);
7ca1d319 424 assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE);
33125ac5 425
6c14ad61 426 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED);
4af7b60d
TG
427 if (r < 0) {
428 const NLTypeSystemUnion *type_system_union;
429 int family;
430
6c14ad61 431 r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION);
4af7b60d
TG
432 if (r < 0)
433 return r;
4af7b60d 434
89489ef7 435 r = sd_rtnl_message_get_family(m, &family);
4af7b60d
TG
436 if (r < 0)
437 return r;
438
f663aeb8 439 r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
4af7b60d
TG
440 if (r < 0)
441 return r;
442
443 r = type_system_union_protocol_get_type_system(type_system_union,
f663aeb8 444 &m->containers[m->n_containers + 1].type_system,
4af7b60d
TG
445 family);
446 if (r < 0)
447 return r;
448 } else {
f663aeb8
TG
449 r = type_system_get_type_system(m->containers[m->n_containers].type_system,
450 &m->containers[m->n_containers + 1].type_system,
4af7b60d
TG
451 type);
452 if (r < 0)
453 return r;
454 }
31a4e153 455
fcf81a54 456 r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
d8e538ec
TG
457 if (r < 0)
458 return r;
459
313cefa1 460 m->containers[m->n_containers++].offset = r;
7ca1d319 461
d8e538ec
TG
462 return 0;
463}
464
1c4baffc 465int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) {
d8e538ec
TG
466 const NLTypeSystemUnion *type_system_union;
467 int r;
468
469 assert_return(m, -EINVAL);
470 assert_return(!m->sealed, -EPERM);
471
f663aeb8 472 r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
d8e538ec
TG
473 if (r < 0)
474 return r;
475
476 r = type_system_union_get_type_system(type_system_union,
f663aeb8 477 &m->containers[m->n_containers + 1].type_system,
d8e538ec
TG
478 key);
479 if (r < 0)
480 return r;
33125ac5 481
1c4baffc 482 r = sd_netlink_message_append_string(m, type_system_union->match, key);
d8e538ec
TG
483 if (r < 0)
484 return r;
485
05d0c2e3 486 /* do we ever need non-null size */
da041d69 487 r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
d8e538ec
TG
488 if (r < 0)
489 return r;
490
313cefa1 491 m->containers[m->n_containers++].offset = r;
7ca1d319 492
d8e538ec 493 return 0;
33125ac5
TG
494}
495
1c4baffc 496int sd_netlink_message_close_container(sd_netlink_message *m) {
33125ac5 497 assert_return(m, -EINVAL);
e5c4350b 498 assert_return(!m->sealed, -EPERM);
5a081409 499 assert_return(m->n_containers > 0, -EINVAL);
33125ac5 500
f663aeb8 501 m->containers[m->n_containers].type_system = NULL;
05d0c2e3
JT
502 m->containers[m->n_containers].offset = 0;
503 m->n_containers--;
504
505 return 0;
506}
507
508int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
509 int r;
510
511 assert_return(m, -EINVAL);
512 assert_return(!m->sealed, -EPERM);
513 assert_return(m->n_containers > 0, -EINVAL);
514
515 r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
516 if (r < 0)
517 return r;
518
519 m->containers[m->n_containers].offset = r;
520 m->n_containers++;
521 m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
522
523 return 0;
524}
525
526int sd_netlink_message_cancel_array(sd_netlink_message *m) {
527 unsigned i;
528 uint32_t rta_len;
529
530 assert_return(m, -EINVAL);
531 assert_return(!m->sealed, -EPERM);
532 assert_return(m->n_containers > 1, -EINVAL);
533
534 rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len;
535
536 for (i = 0; i < m->n_containers; i++)
537 GET_CONTAINER(m, i)->rta_len -= rta_len;
538
539 m->hdr->nlmsg_len -= rta_len;
540
313cefa1 541 m->n_containers--;
05d0c2e3 542 m->containers[m->n_containers].type_system = NULL;
33125ac5
TG
543
544 return 0;
545}
546
4c641e99 547static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) {
f663aeb8 548 struct netlink_attribute *attribute;
f66eeb6b
TG
549 struct rtattr *rta;
550
44caa5e7
SS
551 assert_return(m, -EINVAL);
552 assert_return(m->sealed, -EPERM);
553 assert_return(data, -EINVAL);
f1dd72c2 554
0610939d 555 assert(m->n_containers < RTNL_CONTAINER_DEPTH);
f663aeb8
TG
556 assert(m->containers[m->n_containers].attributes);
557 assert(type < m->containers[m->n_containers].n_attributes);
44caa5e7 558
f663aeb8
TG
559 attribute = &m->containers[m->n_containers].attributes[type];
560
f1dd72c2 561 if (attribute->offset == 0)
44caa5e7
SS
562 return -ENODATA;
563
f663aeb8 564 rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset);
44caa5e7 565
f66eeb6b
TG
566 *data = RTA_DATA(rta);
567
4c641e99
TG
568 if (net_byteorder)
569 *net_byteorder = attribute->net_byteorder;
570
f66eeb6b 571 return RTA_PAYLOAD(rta);
44caa5e7
SS
572}
573
1c4baffc 574int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
44caa5e7
SS
575 int r;
576 void *attr_data;
577
73ae2b7d
TG
578 assert_return(m, -EINVAL);
579
6c14ad61 580 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
d8e538ec
TG
581 if (r < 0)
582 return r;
44caa5e7 583
4c641e99 584 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 585 if (r < 0)
44caa5e7 586 return r;
f66eeb6b
TG
587 else if (strnlen(attr_data, r) >= (size_t) r)
588 return -EIO;
44caa5e7 589
73ae2b7d
TG
590 if (data)
591 *data = (const char *) attr_data;
44caa5e7
SS
592
593 return 0;
594}
595
1c4baffc 596int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
44caa5e7
SS
597 int r;
598 void *attr_data;
599
73ae2b7d
TG
600 assert_return(m, -EINVAL);
601
6c14ad61 602 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
d8e538ec
TG
603 if (r < 0)
604 return r;
44caa5e7 605
4c641e99 606 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 607 if (r < 0)
44caa5e7 608 return r;
f66eeb6b
TG
609 else if ((size_t) r < sizeof(uint8_t))
610 return -EIO;
44caa5e7 611
73ae2b7d
TG
612 if (data)
613 *data = *(uint8_t *) attr_data;
44caa5e7
SS
614
615 return 0;
616}
617
1c4baffc 618int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
44caa5e7 619 void *attr_data;
4c641e99
TG
620 bool net_byteorder;
621 int r;
44caa5e7 622
73ae2b7d
TG
623 assert_return(m, -EINVAL);
624
6c14ad61 625 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
d8e538ec
TG
626 if (r < 0)
627 return r;
44caa5e7 628
4c641e99 629 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 630 if (r < 0)
44caa5e7 631 return r;
f66eeb6b
TG
632 else if ((size_t) r < sizeof(uint16_t))
633 return -EIO;
44caa5e7 634
4c641e99
TG
635 if (data) {
636 if (net_byteorder)
637 *data = be16toh(*(uint16_t *) attr_data);
638 else
639 *data = *(uint16_t *) attr_data;
640 }
44caa5e7
SS
641
642 return 0;
643}
644
1c4baffc 645int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
44caa5e7 646 void *attr_data;
4c641e99
TG
647 bool net_byteorder;
648 int r;
44caa5e7 649
73ae2b7d
TG
650 assert_return(m, -EINVAL);
651
6c14ad61 652 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
d8e538ec
TG
653 if (r < 0)
654 return r;
44caa5e7 655
4c641e99 656 r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
f66eeb6b 657 if (r < 0)
44caa5e7 658 return r;
f66eeb6b
TG
659 else if ((size_t)r < sizeof(uint32_t))
660 return -EIO;
44caa5e7 661
4c641e99
TG
662 if (data) {
663 if (net_byteorder)
664 *data = be32toh(*(uint32_t *) attr_data);
665 else
666 *data = *(uint32_t *) attr_data;
667 }
44caa5e7
SS
668
669 return 0;
670}
671
1c4baffc 672int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
4e9e7f18
SS
673 int r;
674 void *attr_data;
675
73ae2b7d
TG
676 assert_return(m, -EINVAL);
677
6c14ad61 678 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
d8e538ec
TG
679 if (r < 0)
680 return r;
4e9e7f18 681
4c641e99 682 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 683 if (r < 0)
4e9e7f18 684 return r;
f66eeb6b
TG
685 else if ((size_t)r < sizeof(struct ether_addr))
686 return -EIO;
4e9e7f18 687
73ae2b7d
TG
688 if (data)
689 memcpy(data, attr_data, sizeof(struct ether_addr));
4e9e7f18
SS
690
691 return 0;
692}
693
1c4baffc 694int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
aba496a5
UTL
695 int r;
696 void *attr_data;
697
73ae2b7d
TG
698 assert_return(m, -EINVAL);
699
6c14ad61 700 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
aba496a5
UTL
701 if (r < 0)
702 return r;
703
4c641e99 704 r = netlink_message_read_internal(m, type, &attr_data, NULL);
aba496a5
UTL
705 if (r < 0)
706 return r;
707 else if ((size_t)r < sizeof(struct ifa_cacheinfo))
708 return -EIO;
709
73ae2b7d
TG
710 if (info)
711 memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
aba496a5
UTL
712
713 return 0;
714}
715
1c4baffc 716int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
4e9e7f18
SS
717 int r;
718 void *attr_data;
719
73ae2b7d
TG
720 assert_return(m, -EINVAL);
721
6c14ad61 722 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
d8e538ec
TG
723 if (r < 0)
724 return r;
4e9e7f18 725
4c641e99 726 r = netlink_message_read_internal(m, type, &attr_data, NULL);
f66eeb6b 727 if (r < 0)
4e9e7f18 728 return r;
f66eeb6b
TG
729 else if ((size_t)r < sizeof(struct in_addr))
730 return -EIO;
4e9e7f18 731
73ae2b7d
TG
732 if (data)
733 memcpy(data, attr_data, sizeof(struct in_addr));
4e9e7f18
SS
734
735 return 0;
736}
737
1c4baffc 738int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
4e9e7f18
SS
739 int r;
740 void *attr_data;
741
73ae2b7d
TG
742 assert_return(m, -EINVAL);
743
6c14ad61 744 r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
d8e538ec
TG
745 if (r < 0)
746 return r;
4e9e7f18 747
4c641e99 748 r = netlink_message_read_internal(m, type, &attr_data, NULL);
3dd215e0 749 if (r < 0)
4e9e7f18 750 return r;
3dd215e0
TG
751 else if ((size_t)r < sizeof(struct in6_addr))
752 return -EIO;
4e9e7f18 753
73ae2b7d
TG
754 if (data)
755 memcpy(data, attr_data, sizeof(struct in6_addr));
4e9e7f18
SS
756
757 return 0;
758}
759
f663aeb8
TG
760static int netlink_container_parse(sd_netlink_message *m,
761 struct netlink_container *container,
762 int count,
763 struct rtattr *rta,
764 unsigned int rt_len) {
765 _cleanup_free_ struct netlink_attribute *attributes = NULL;
4203fc8b 766
f663aeb8 767 attributes = new0(struct netlink_attribute, count);
9ed794a3 768 if (!attributes)
4203fc8b
TG
769 return -ENOMEM;
770
4203fc8b 771 for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) {
f663aeb8
TG
772 unsigned short type;
773
4203fc8b
TG
774 type = RTA_TYPE(rta);
775
776 /* if the kernel is newer than the headers we used
f663aeb8 777 when building, we ignore out-of-range attributes */
4203fc8b
TG
778 if (type >= count)
779 continue;
780
f1dd72c2 781 if (attributes[type].offset != 0)
4203fc8b
TG
782 log_debug("rtnl: message parse - overwriting repeated attribute");
783
f663aeb8 784 attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
4c641e99
TG
785 attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
786 attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
4203fc8b
TG
787 }
788
ae2a15bc 789 container->attributes = TAKE_PTR(attributes);
f663aeb8 790 container->n_attributes = count;
4203fc8b
TG
791
792 return 0;
793}
794
817d1cd8 795int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
d8e538ec
TG
796 const NLType *nl_type;
797 const NLTypeSystem *type_system;
3dd215e0 798 void *container;
817d1cd8 799 uint16_t type;
d8e538ec
TG
800 size_t size;
801 int r;
3dd215e0
TG
802
803 assert_return(m, -EINVAL);
804 assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL);
805
f663aeb8 806 r = type_system_get_type(m->containers[m->n_containers].type_system,
d8e538ec 807 &nl_type,
817d1cd8 808 type_id);
3dd215e0
TG
809 if (r < 0)
810 return r;
3dd215e0 811
817d1cd8
DH
812 type = type_get_type(nl_type);
813
814 if (type == NETLINK_TYPE_NESTED) {
f663aeb8 815 r = type_system_get_type_system(m->containers[m->n_containers].type_system,
d8e538ec 816 &type_system,
817d1cd8 817 type_id);
d8e538ec
TG
818 if (r < 0)
819 return r;
817d1cd8 820 } else if (type == NETLINK_TYPE_UNION) {
d8e538ec 821 const NLTypeSystemUnion *type_system_union;
d8e538ec 822
f663aeb8 823 r = type_system_get_type_system_union(m->containers[m->n_containers].type_system,
d8e538ec 824 &type_system_union,
817d1cd8 825 type_id);
d8e538ec
TG
826 if (r < 0)
827 return r;
828
4af7b60d
TG
829 switch (type_system_union->match_type) {
830 case NL_MATCH_SIBLING:
831 {
832 const char *key;
833
1c4baffc 834 r = sd_netlink_message_read_string(m, type_system_union->match, &key);
4af7b60d
TG
835 if (r < 0)
836 return r;
837
838 r = type_system_union_get_type_system(type_system_union,
839 &type_system,
840 key);
841 if (r < 0)
842 return r;
843
844 break;
845 }
846 case NL_MATCH_PROTOCOL:
847 {
848 int family;
849
89489ef7 850 r = sd_rtnl_message_get_family(m, &family);
4af7b60d
TG
851 if (r < 0)
852 return r;
853
854 r = type_system_union_protocol_get_type_system(type_system_union,
855 &type_system,
856 family);
857 if (r < 0)
858 return r;
859
860 break;
861 }
862 default:
1c4baffc 863 assert_not_reached("sd-netlink: invalid type system union type");
4af7b60d 864 }
d8e538ec
TG
865 } else
866 return -EINVAL;
867
4c641e99 868 r = netlink_message_read_internal(m, type_id, &container, NULL);
3dd215e0
TG
869 if (r < 0)
870 return r;
d8e538ec
TG
871 else
872 size = (size_t)r;
3dd215e0 873
313cefa1 874 m->n_containers++;
3dd215e0 875
f663aeb8
TG
876 r = netlink_container_parse(m,
877 &m->containers[m->n_containers],
878 type_system_get_count(type_system),
879 container,
880 size);
d8e538ec 881 if (r < 0) {
313cefa1 882 m->n_containers--;
3dd215e0 883 return r;
d8e538ec 884 }
3dd215e0 885
f663aeb8 886 m->containers[m->n_containers].type_system = type_system;
3dd215e0
TG
887
888 return 0;
889}
890
1c4baffc 891int sd_netlink_message_exit_container(sd_netlink_message *m) {
e5c4350b
TG
892 assert_return(m, -EINVAL);
893 assert_return(m->sealed, -EINVAL);
894 assert_return(m->n_containers > 0, -EINVAL);
895
a1e58e8e 896 m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes);
f663aeb8 897 m->containers[m->n_containers].type_system = NULL;
3dd215e0 898
313cefa1 899 m->n_containers--;
e5c4350b
TG
900
901 return 0;
902}
903
1c4baffc 904uint32_t rtnl_message_get_serial(sd_netlink_message *m) {
65f568bb 905 assert(m);
9d0db178 906 assert(m->hdr);
65f568bb
TG
907
908 return m->hdr->nlmsg_seq;
909}
910
1c4baffc 911int sd_netlink_message_is_error(sd_netlink_message *m) {
45af44d4
TG
912 assert_return(m, 0);
913 assert_return(m->hdr, 0);
914
915 return m->hdr->nlmsg_type == NLMSG_ERROR;
916}
917
1c4baffc 918int sd_netlink_message_get_errno(sd_netlink_message *m) {
65f568bb
TG
919 struct nlmsgerr *err;
920
e16bcf98 921 assert_return(m, -EINVAL);
9d0db178 922 assert_return(m->hdr, -EINVAL);
65f568bb 923
1c4baffc 924 if (!sd_netlink_message_is_error(m))
65f568bb
TG
925 return 0;
926
927 err = NLMSG_DATA(m->hdr);
928
929 return err->error;
930}
931
1c4baffc 932int sd_netlink_message_rewind(sd_netlink_message *m) {
817d1cd8 933 const NLType *nl_type;
05d0c2e3 934 const NLTypeSystem *type_system_root;
817d1cd8
DH
935 uint16_t type;
936 size_t size;
3dd215e0
TG
937 unsigned i;
938 int r;
0fc7531b
TG
939
940 assert_return(m, -EINVAL);
0fc7531b 941
3dd215e0
TG
942 /* don't allow appending to message once parsed */
943 if (!m->sealed)
944 rtnl_message_seal(m);
945
05d0c2e3
JT
946 type_system_root = type_system_get_root(m->protocol);
947
1f6b4113 948 for (i = 1; i <= m->n_containers; i++)
a1e58e8e 949 m->containers[i].attributes = mfree(m->containers[i].attributes);
3dd215e0
TG
950
951 m->n_containers = 0;
952
ece174c5 953 if (m->containers[0].attributes)
3dd215e0
TG
954 /* top-level attributes have already been parsed */
955 return 0;
3dd215e0 956
d8e538ec
TG
957 assert(m->hdr);
958
05d0c2e3 959 r = type_system_get_type(type_system_root, &nl_type, m->hdr->nlmsg_type);
d8e538ec
TG
960 if (r < 0)
961 return r;
962
817d1cd8
DH
963 type = type_get_type(nl_type);
964 size = type_get_size(nl_type);
965
966 if (type == NETLINK_TYPE_NESTED) {
c658008f 967 const NLTypeSystem *type_system;
d8e538ec 968
817d1cd8 969 type_get_type_system(nl_type, &type_system);
d8e538ec 970
f663aeb8 971 m->containers[0].type_system = type_system;
d8e538ec 972
f663aeb8
TG
973 r = netlink_container_parse(m,
974 &m->containers[m->n_containers],
975 type_system_get_count(type_system),
976 (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
977 NLMSG_PAYLOAD(m->hdr, size));
d8e538ec
TG
978 if (r < 0)
979 return r;
0fc7531b
TG
980 }
981
982 return 0;
983}
3dd215e0 984
1c4baffc 985void rtnl_message_seal(sd_netlink_message *m) {
3dd215e0
TG
986 assert(m);
987 assert(!m->sealed);
988
989 m->sealed = true;
990}
1403f45a 991
1c4baffc 992sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) {
1403f45a
TG
993 assert_return(m, NULL);
994
995 return m->next;
996}