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