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