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