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