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