]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
65f568bb TG |
2 | |
3 | #include <netinet/in.h> | |
65f568bb TG |
4 | #include <stdbool.h> |
5 | #include <unistd.h> | |
6 | ||
07630cea LP |
7 | #include "sd-netlink.h" |
8 | ||
b5efdb8a | 9 | #include "alloc-util.h" |
f97b34a6 | 10 | #include "format-util.h" |
8f3c1859 | 11 | #include "memory-util.h" |
1c4baffc TG |
12 | #include "netlink-internal.h" |
13 | #include "netlink-types.h" | |
07630cea | 14 | #include "netlink-util.h" |
07630cea | 15 | #include "socket-util.h" |
8f3c1859 | 16 | #include "strv.h" |
65f568bb | 17 | |
179b4db4 | 18 | #define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset)) |
4ebe732c | 19 | |
0a2478a9 | 20 | #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) |
4c641e99 | 21 | #define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) |
0a2478a9 | 22 | |
409856d3 | 23 | int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) { |
1c4baffc | 24 | sd_netlink_message *m; |
65f568bb | 25 | |
1cedca05 YW |
26 | assert(nl); |
27 | assert(ret); | |
65f568bb | 28 | |
409856d3 YW |
29 | /* Note that 'nl' is currently unused, if we start using it internally we must take care to |
30 | * avoid problems due to mutual references between buses and their queued messages. See sd-bus. */ | |
8c578303 | 31 | |
6bf8e24b | 32 | m = new(sd_netlink_message, 1); |
65f568bb TG |
33 | if (!m) |
34 | return -ENOMEM; | |
35 | ||
6bf8e24b YW |
36 | *m = (sd_netlink_message) { |
37 | .n_ref = 1, | |
409856d3 | 38 | .protocol = nl->protocol, |
6bf8e24b YW |
39 | .sealed = false, |
40 | }; | |
65f568bb TG |
41 | |
42 | *ret = m; | |
65f568bb TG |
43 | return 0; |
44 | } | |
45 | ||
1cedca05 YW |
46 | int message_new_full( |
47 | sd_netlink *nl, | |
48 | uint16_t nlmsg_type, | |
49 | const NLTypeSystem *type_system, | |
50 | size_t header_size, | |
51 | sd_netlink_message **ret) { | |
52 | ||
4afd3348 | 53 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
d8e538ec TG |
54 | size_t size; |
55 | int r; | |
56 | ||
1cedca05 YW |
57 | assert(nl); |
58 | assert(type_system); | |
59 | assert(ret); | |
d8e538ec | 60 | |
1cedca05 YW |
61 | size = NLMSG_SPACE(header_size); |
62 | assert(size >= sizeof(struct nlmsghdr)); | |
12b7dff4 | 63 | |
409856d3 | 64 | r = message_new_empty(nl, &m); |
d8e538ec TG |
65 | if (r < 0) |
66 | return r; | |
67 | ||
1cedca05 | 68 | m->containers[0].type_system = type_system; |
1b89cf56 | 69 | |
1b89cf56 TG |
70 | m->hdr = malloc0(size); |
71 | if (!m->hdr) | |
72 | return -ENOMEM; | |
73 | ||
74 | m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; | |
1b89cf56 | 75 | m->hdr->nlmsg_len = size; |
1cedca05 | 76 | m->hdr->nlmsg_type = nlmsg_type; |
1b89cf56 | 77 | |
1cc6c93a | 78 | *ret = TAKE_PTR(m); |
d8e538ec TG |
79 | return 0; |
80 | } | |
81 | ||
1cedca05 | 82 | int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) { |
56fdc16d YW |
83 | const NLTypeSystem *type_system; |
84 | size_t size; | |
1cedca05 YW |
85 | int r; |
86 | ||
87 | assert_return(nl, -EINVAL); | |
88 | assert_return(ret, -EINVAL); | |
89 | ||
56fdc16d | 90 | r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size); |
1cedca05 YW |
91 | if (r < 0) |
92 | return r; | |
93 | ||
56fdc16d | 94 | return message_new_full(nl, type, type_system, size, ret); |
1cedca05 YW |
95 | } |
96 | ||
409856d3 YW |
97 | int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) { |
98 | struct nlmsgerr *err; | |
99 | int r; | |
100 | ||
101 | assert(error <= 0); | |
102 | ||
103 | r = message_new(nl, ret, NLMSG_ERROR); | |
104 | if (r < 0) | |
105 | return r; | |
106 | ||
107 | message_seal(*ret); | |
108 | (*ret)->hdr->nlmsg_seq = serial; | |
109 | ||
110 | err = NLMSG_DATA((*ret)->hdr); | |
111 | err->error = error; | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
1c4baffc | 116 | int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { |
6e20c8f8 TG |
117 | assert_return(m, -EINVAL); |
118 | assert_return(m->hdr, -EINVAL); | |
ec44d3f4 YW |
119 | assert_return(m->protocol != NETLINK_ROUTE || |
120 | IN_SET(m->hdr->nlmsg_type, | |
ffeb16f5 | 121 | RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, |
e8b8b3ea YW |
122 | RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP, RTM_GETQDISC, RTM_GETTCLASS), |
123 | -EINVAL); | |
6e20c8f8 | 124 | |
5883ff60 | 125 | SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); |
6e20c8f8 TG |
126 | |
127 | return 0; | |
128 | } | |
129 | ||
f23ab4dc | 130 | DEFINE_TRIVIAL_REF_FUNC(sd_netlink_message, sd_netlink_message); |
65f568bb | 131 | |
1c4baffc | 132 | sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { |
f23ab4dc | 133 | while (m && --m->n_ref == 0) { |
3dd215e0 TG |
134 | unsigned i; |
135 | ||
65f568bb | 136 | free(m->hdr); |
3dd215e0 | 137 | |
9f5bbfe3 | 138 | for (i = 0; i <= m->n_containers; i++) |
f663aeb8 | 139 | free(m->containers[i].attributes); |
3dd215e0 | 140 | |
f23ab4dc | 141 | sd_netlink_message *t = m; |
82e4eda6 DH |
142 | m = m->next; |
143 | free(t); | |
65f568bb TG |
144 | } |
145 | ||
146 | return NULL; | |
147 | } | |
148 | ||
041ea9f9 | 149 | int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) { |
65f568bb | 150 | assert_return(m, -EINVAL); |
a22a8698 | 151 | assert_return(type != 0, -EINVAL); |
65f568bb TG |
152 | |
153 | *type = m->hdr->nlmsg_type; | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
c06cb593 SS |
158 | int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) { |
159 | assert_return(m, -EINVAL); | |
a22a8698 | 160 | assert_return(flags != 0, -EINVAL); |
c06cb593 SS |
161 | |
162 | m->hdr->nlmsg_flags = flags; | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
041ea9f9 | 167 | int sd_netlink_message_is_broadcast(sd_netlink_message *m) { |
1f0db3ed TG |
168 | assert_return(m, -EINVAL); |
169 | ||
4d4d898a | 170 | return m->multicast_group != 0; |
1f0db3ed TG |
171 | } |
172 | ||
4ebe732c ZJS |
173 | /* If successful the updated message will be correctly aligned, if |
174 | unsuccessful the old message is untouched. */ | |
1c4baffc | 175 | static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) { |
6497a8aa | 176 | size_t message_length; |
65f568bb TG |
177 | struct nlmsghdr *new_hdr; |
178 | struct rtattr *rta; | |
7ca1d319 | 179 | int offset; |
65f568bb | 180 | |
33125ac5 TG |
181 | assert(m); |
182 | assert(m->hdr); | |
e5c4350b | 183 | assert(!m->sealed); |
33125ac5 | 184 | assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); |
6497a8aa | 185 | assert(!data || data_length > 0); |
4ebe732c ZJS |
186 | |
187 | /* get the new message size (with padding at the end) */ | |
6497a8aa | 188 | message_length = m->hdr->nlmsg_len + RTA_SPACE(data_length); |
65f568bb | 189 | |
05d0c2e3 JT |
190 | /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ |
191 | if (message_length > MIN(page_size(), 8192UL)) | |
192 | return -ENOBUFS; | |
193 | ||
65f568bb TG |
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 */ | |
6497a8aa YW |
201 | rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len); |
202 | ||
203 | rtattr_append_attribute_internal(rta, type, data, data_length); | |
65f568bb | 204 | |
5a081409 | 205 | /* if we are inside containers, extend them */ |
c7209bcf | 206 | for (unsigned i = 0; i < m->n_containers; i++) |
6497a8aa | 207 | GET_CONTAINER(m, i)->rta_len += RTA_SPACE(data_length); |
7ca1d319 | 208 | |
4ebe732c | 209 | /* update message size */ |
6497a8aa | 210 | offset = m->hdr->nlmsg_len; |
4ebe732c ZJS |
211 | m->hdr->nlmsg_len = message_length; |
212 | ||
6497a8aa | 213 | /* return old message size */ |
7ca1d319 | 214 | return offset; |
65f568bb TG |
215 | } |
216 | ||
6c14ad61 | 217 | static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) { |
d8e538ec | 218 | const NLType *type; |
d8e538ec | 219 | |
6c14ad61 DH |
220 | assert(m); |
221 | ||
98be4292 YW |
222 | type = type_system_get_type(m->containers[m->n_containers].type_system, attribute_type); |
223 | if (!type) | |
224 | return -EOPNOTSUPP; | |
d8e538ec | 225 | |
817d1cd8 | 226 | if (type_get_type(type) != data_type) |
d8e538ec TG |
227 | return -EINVAL; |
228 | ||
6c14ad61 DH |
229 | if (out_size) |
230 | *out_size = type_get_size(type); | |
231 | return 0; | |
d8e538ec TG |
232 | } |
233 | ||
1c4baffc | 234 | int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) { |
d8e538ec | 235 | size_t length, size; |
0a0dc69b | 236 | int r; |
65f568bb TG |
237 | |
238 | assert_return(m, -EINVAL); | |
e5c4350b | 239 | assert_return(!m->sealed, -EPERM); |
65f568bb TG |
240 | assert_return(data, -EINVAL); |
241 | ||
6c14ad61 | 242 | r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); |
0a0dc69b TG |
243 | if (r < 0) |
244 | return r; | |
65f568bb | 245 | |
d8e538ec | 246 | if (size) { |
3072eecf LP |
247 | length = strnlen(data, size+1); |
248 | if (length > size) | |
d8e538ec TG |
249 | return -EINVAL; |
250 | } else | |
251 | length = strlen(data); | |
33125ac5 | 252 | |
d8e538ec | 253 | r = add_rtattr(m, type, data, length + 1); |
0a0dc69b TG |
254 | if (r < 0) |
255 | return r; | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
6d725977 YW |
260 | int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data) { |
261 | size_t length, size; | |
262 | char * const *p; | |
263 | int r; | |
264 | ||
265 | assert_return(m, -EINVAL); | |
266 | assert_return(!m->sealed, -EPERM); | |
267 | assert_return(data, -EINVAL); | |
268 | ||
269 | r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); | |
270 | if (r < 0) | |
271 | return r; | |
272 | ||
273 | STRV_FOREACH(p, data) { | |
274 | if (size) { | |
275 | length = strnlen(*p, size+1); | |
276 | if (length > size) | |
277 | return -EINVAL; | |
278 | } else | |
279 | length = strlen(*p); | |
280 | ||
281 | r = add_rtattr(m, type, *p, length + 1); | |
282 | if (r < 0) | |
283 | return r; | |
284 | } | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
c06aead0 SS |
289 | int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) { |
290 | size_t size; | |
291 | int r; | |
292 | ||
293 | assert_return(m, -EINVAL); | |
294 | assert_return(!m->sealed, -EPERM); | |
295 | ||
296 | r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG); | |
297 | if (r < 0) | |
298 | return r; | |
299 | ||
300 | r = add_rtattr(m, type, NULL, 0); | |
301 | if (r < 0) | |
302 | return r; | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
1c4baffc | 307 | int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) { |
7b179640 SS |
308 | int r; |
309 | ||
310 | assert_return(m, -EINVAL); | |
311 | assert_return(!m->sealed, -EPERM); | |
312 | ||
6c14ad61 | 313 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); |
7b179640 SS |
314 | if (r < 0) |
315 | return r; | |
316 | ||
7b179640 SS |
317 | r = add_rtattr(m, type, &data, sizeof(uint8_t)); |
318 | if (r < 0) | |
319 | return r; | |
320 | ||
321 | return 0; | |
322 | } | |
323 | ||
1c4baffc | 324 | int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) { |
01b36069 TG |
325 | int r; |
326 | ||
327 | assert_return(m, -EINVAL); | |
e5c4350b | 328 | assert_return(!m->sealed, -EPERM); |
01b36069 | 329 | |
6c14ad61 | 330 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); |
01b36069 TG |
331 | if (r < 0) |
332 | return r; | |
333 | ||
01b36069 TG |
334 | r = add_rtattr(m, type, &data, sizeof(uint16_t)); |
335 | if (r < 0) | |
336 | return r; | |
337 | ||
338 | return 0; | |
339 | } | |
340 | ||
1c4baffc | 341 | int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) { |
0a0dc69b TG |
342 | int r; |
343 | ||
344 | assert_return(m, -EINVAL); | |
e5c4350b | 345 | assert_return(!m->sealed, -EPERM); |
0a0dc69b | 346 | |
6c14ad61 | 347 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); |
0a0dc69b TG |
348 | if (r < 0) |
349 | return r; | |
350 | ||
4d47756b | 351 | r = add_rtattr(m, type, &data, sizeof(uint32_t)); |
0a0dc69b TG |
352 | if (r < 0) |
353 | return r; | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
81962db7 SS |
358 | int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data) { |
359 | int r; | |
360 | ||
361 | assert_return(m, -EINVAL); | |
362 | assert_return(!m->sealed, -EPERM); | |
363 | ||
364 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U64); | |
365 | if (r < 0) | |
366 | return r; | |
367 | ||
368 | r = add_rtattr(m, type, &data, sizeof(uint64_t)); | |
369 | if (r < 0) | |
370 | return r; | |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
aa550d2a SS |
375 | int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) { |
376 | int r; | |
377 | ||
378 | assert_return(m, -EINVAL); | |
379 | assert_return(!m->sealed, -EPERM); | |
380 | ||
381 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8); | |
382 | if (r < 0) | |
383 | return r; | |
384 | ||
385 | r = add_rtattr(m, type, &data, sizeof(int8_t)); | |
386 | if (r < 0) | |
387 | return r; | |
388 | ||
389 | return 0; | |
390 | } | |
391 | ||
392 | int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) { | |
393 | int r; | |
394 | ||
395 | assert_return(m, -EINVAL); | |
396 | assert_return(!m->sealed, -EPERM); | |
397 | ||
398 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16); | |
399 | if (r < 0) | |
400 | return r; | |
401 | ||
402 | r = add_rtattr(m, type, &data, sizeof(int16_t)); | |
403 | if (r < 0) | |
404 | return r; | |
405 | ||
406 | return 0; | |
407 | } | |
408 | ||
409 | int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) { | |
410 | int r; | |
411 | ||
412 | assert_return(m, -EINVAL); | |
413 | assert_return(!m->sealed, -EPERM); | |
414 | ||
415 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32); | |
416 | if (r < 0) | |
417 | return r; | |
418 | ||
419 | r = add_rtattr(m, type, &data, sizeof(int32_t)); | |
420 | if (r < 0) | |
421 | return r; | |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
426 | int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) { | |
427 | int r; | |
428 | ||
429 | assert_return(m, -EINVAL); | |
430 | assert_return(!m->sealed, -EPERM); | |
431 | ||
432 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64); | |
433 | if (r < 0) | |
434 | return r; | |
435 | ||
436 | r = add_rtattr(m, type, &data, sizeof(int64_t)); | |
437 | if (r < 0) | |
438 | return r; | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
17af840b SS |
443 | int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { |
444 | int r; | |
445 | ||
446 | assert_return(m, -EINVAL); | |
447 | assert_return(!m->sealed, -EPERM); | |
448 | ||
b8cc01a2 | 449 | r = add_rtattr(m, type, data, len); |
17af840b SS |
450 | if (r < 0) |
451 | return r; | |
452 | ||
453 | return 0; | |
454 | } | |
455 | ||
67b19a49 | 456 | int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data) { |
0a0dc69b TG |
457 | int r; |
458 | ||
459 | assert_return(m, -EINVAL); | |
e5c4350b | 460 | assert_return(!m->sealed, -EPERM); |
0a0dc69b | 461 | assert_return(data, -EINVAL); |
67b19a49 | 462 | assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL); |
0a0dc69b | 463 | |
6c14ad61 | 464 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); |
0a0dc69b TG |
465 | if (r < 0) |
466 | return r; | |
467 | ||
67b19a49 | 468 | r = add_rtattr(m, type, data, FAMILY_ADDRESS_SIZE(family)); |
0a0dc69b TG |
469 | if (r < 0) |
470 | return r; | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
67b19a49 YW |
475 | int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { |
476 | return netlink_message_append_in_addr_union(m, type, AF_INET, (const union in_addr_union *) data); | |
477 | } | |
abd48ec8 | 478 | |
67b19a49 YW |
479 | int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) { |
480 | return netlink_message_append_in_addr_union(m, type, AF_INET6, (const union in_addr_union *) data); | |
abd48ec8 YW |
481 | } |
482 | ||
67b19a49 | 483 | int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data) { |
abd48ec8 YW |
484 | int r; |
485 | ||
486 | assert_return(m, -EINVAL); | |
487 | assert_return(!m->sealed, -EPERM); | |
488 | assert_return(data, -EINVAL); | |
67b19a49 | 489 | assert_return(IN_SET(data->sa.sa_family, AF_INET, AF_INET6), -EINVAL); |
abd48ec8 YW |
490 | |
491 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR); | |
492 | if (r < 0) | |
493 | return r; | |
494 | ||
67b19a49 | 495 | r = add_rtattr(m, type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); |
abd48ec8 YW |
496 | if (r < 0) |
497 | return r; | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
67b19a49 YW |
502 | int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data) { |
503 | return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data); | |
504 | } | |
0a0dc69b | 505 | |
67b19a49 YW |
506 | int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data) { |
507 | return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data); | |
0a0dc69b TG |
508 | } |
509 | ||
1c4baffc | 510 | int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { |
0a0dc69b TG |
511 | int r; |
512 | ||
513 | assert_return(m, -EINVAL); | |
e5c4350b | 514 | assert_return(!m->sealed, -EPERM); |
0a0dc69b TG |
515 | assert_return(data, -EINVAL); |
516 | ||
6c14ad61 | 517 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); |
d8e538ec TG |
518 | if (r < 0) |
519 | return r; | |
0a0dc69b | 520 | |
b9eaf3d1 | 521 | r = add_rtattr(m, type, data, ETH_ALEN); |
0a0dc69b TG |
522 | if (r < 0) |
523 | return r; | |
524 | ||
525 | return 0; | |
65f568bb TG |
526 | } |
527 | ||
ca2b7cd8 | 528 | int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data) { |
4fc8a29a TR |
529 | int r; |
530 | ||
531 | assert_return(m, -EINVAL); | |
532 | assert_return(!m->sealed, -EPERM); | |
533 | assert_return(data, -EINVAL); | |
534 | assert_return(data->length > 0, -EINVAL); | |
535 | ||
536 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); | |
537 | if (r < 0) | |
538 | return r; | |
539 | ||
ca2b7cd8 | 540 | r = add_rtattr(m, type, data->bytes, data->length); |
4fc8a29a TR |
541 | if (r < 0) |
542 | return r; | |
543 | ||
544 | return 0; | |
545 | } | |
546 | ||
1c4baffc | 547 | int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { |
aba496a5 UTL |
548 | int r; |
549 | ||
550 | assert_return(m, -EINVAL); | |
551 | assert_return(!m->sealed, -EPERM); | |
552 | assert_return(info, -EINVAL); | |
553 | ||
6c14ad61 | 554 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); |
aba496a5 UTL |
555 | if (r < 0) |
556 | return r; | |
557 | ||
558 | r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo)); | |
559 | if (r < 0) | |
560 | return r; | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
1c4baffc | 565 | int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) { |
d8e538ec TG |
566 | size_t size; |
567 | int r; | |
33125ac5 | 568 | |
65f568bb | 569 | assert_return(m, -EINVAL); |
e5c4350b | 570 | assert_return(!m->sealed, -EPERM); |
179b4db4 | 571 | /* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */ |
409856d3 | 572 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE); |
33125ac5 | 573 | |
6c14ad61 | 574 | r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED); |
4af7b60d TG |
575 | if (r < 0) { |
576 | const NLTypeSystemUnion *type_system_union; | |
577 | int family; | |
578 | ||
6c14ad61 | 579 | r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION); |
4af7b60d TG |
580 | if (r < 0) |
581 | return r; | |
4af7b60d | 582 | |
89489ef7 | 583 | r = sd_rtnl_message_get_family(m, &family); |
4af7b60d TG |
584 | if (r < 0) |
585 | return r; | |
586 | ||
98be4292 YW |
587 | type_system_union = type_system_get_type_system_union( |
588 | m->containers[m->n_containers].type_system, | |
589 | type); | |
590 | if (!type_system_union) | |
591 | return -EOPNOTSUPP; | |
4af7b60d | 592 | |
98be4292 YW |
593 | m->containers[m->n_containers + 1].type_system = |
594 | type_system_union_get_type_system_by_protocol( | |
ea073c8f | 595 | type_system_union, |
ea073c8f | 596 | family); |
98be4292 YW |
597 | } else |
598 | m->containers[m->n_containers + 1].type_system = | |
599 | type_system_get_type_system( | |
600 | m->containers[m->n_containers].type_system, | |
601 | type); | |
602 | if (!m->containers[m->n_containers + 1].type_system) | |
603 | return -EOPNOTSUPP; | |
31a4e153 | 604 | |
fcf81a54 | 605 | r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); |
d8e538ec TG |
606 | if (r < 0) |
607 | return r; | |
608 | ||
313cefa1 | 609 | m->containers[m->n_containers++].offset = r; |
7ca1d319 | 610 | |
d8e538ec TG |
611 | return 0; |
612 | } | |
613 | ||
1c4baffc | 614 | int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) { |
d8e538ec TG |
615 | const NLTypeSystemUnion *type_system_union; |
616 | int r; | |
617 | ||
618 | assert_return(m, -EINVAL); | |
619 | assert_return(!m->sealed, -EPERM); | |
409856d3 | 620 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE); |
d8e538ec | 621 | |
98be4292 YW |
622 | type_system_union = type_system_get_type_system_union( |
623 | m->containers[m->n_containers].type_system, | |
624 | type); | |
625 | if (!type_system_union) | |
626 | return -EOPNOTSUPP; | |
d8e538ec | 627 | |
98be4292 YW |
628 | m->containers[m->n_containers + 1].type_system = |
629 | type_system_union_get_type_system_by_string( | |
ea073c8f | 630 | type_system_union, |
ea073c8f | 631 | key); |
98be4292 YW |
632 | if (!m->containers[m->n_containers + 1].type_system) |
633 | return -EOPNOTSUPP; | |
33125ac5 | 634 | |
b019c545 | 635 | r = sd_netlink_message_append_string(m, type_system_union_get_match_attribute(type_system_union), key); |
d8e538ec TG |
636 | if (r < 0) |
637 | return r; | |
638 | ||
05d0c2e3 | 639 | /* do we ever need non-null size */ |
da041d69 | 640 | r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); |
d8e538ec TG |
641 | if (r < 0) |
642 | return r; | |
643 | ||
313cefa1 | 644 | m->containers[m->n_containers++].offset = r; |
7ca1d319 | 645 | |
d8e538ec | 646 | return 0; |
33125ac5 TG |
647 | } |
648 | ||
1c4baffc | 649 | int sd_netlink_message_close_container(sd_netlink_message *m) { |
33125ac5 | 650 | assert_return(m, -EINVAL); |
e5c4350b | 651 | assert_return(!m->sealed, -EPERM); |
5a081409 | 652 | assert_return(m->n_containers > 0, -EINVAL); |
33125ac5 | 653 | |
f663aeb8 | 654 | m->containers[m->n_containers].type_system = NULL; |
05d0c2e3 JT |
655 | m->containers[m->n_containers].offset = 0; |
656 | m->n_containers--; | |
657 | ||
658 | return 0; | |
659 | } | |
660 | ||
661 | int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) { | |
662 | int r; | |
663 | ||
664 | assert_return(m, -EINVAL); | |
665 | assert_return(!m->sealed, -EPERM); | |
409856d3 | 666 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE); |
05d0c2e3 JT |
667 | |
668 | r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); | |
669 | if (r < 0) | |
670 | return r; | |
671 | ||
672 | m->containers[m->n_containers].offset = r; | |
673 | m->n_containers++; | |
674 | m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system; | |
675 | ||
676 | return 0; | |
677 | } | |
678 | ||
679 | int sd_netlink_message_cancel_array(sd_netlink_message *m) { | |
05d0c2e3 JT |
680 | uint32_t rta_len; |
681 | ||
682 | assert_return(m, -EINVAL); | |
683 | assert_return(!m->sealed, -EPERM); | |
684 | assert_return(m->n_containers > 1, -EINVAL); | |
685 | ||
686 | rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len; | |
687 | ||
c7209bcf | 688 | for (unsigned i = 0; i < m->n_containers; i++) |
05d0c2e3 JT |
689 | GET_CONTAINER(m, i)->rta_len -= rta_len; |
690 | ||
691 | m->hdr->nlmsg_len -= rta_len; | |
692 | ||
313cefa1 | 693 | m->n_containers--; |
05d0c2e3 | 694 | m->containers[m->n_containers].type_system = NULL; |
33125ac5 TG |
695 | |
696 | return 0; | |
697 | } | |
698 | ||
3b4e3ebb YW |
699 | static int netlink_message_read_internal( |
700 | sd_netlink_message *m, | |
701 | unsigned short type, | |
702 | void **ret_data, | |
703 | bool *ret_net_byteorder) { | |
704 | ||
f663aeb8 | 705 | struct netlink_attribute *attribute; |
f66eeb6b TG |
706 | struct rtattr *rta; |
707 | ||
44caa5e7 SS |
708 | assert_return(m, -EINVAL); |
709 | assert_return(m->sealed, -EPERM); | |
f1dd72c2 | 710 | |
409856d3 | 711 | assert(m->n_containers < NETLINK_CONTAINER_DEPTH); |
e4a1e68d YW |
712 | |
713 | if (!m->containers[m->n_containers].attributes) | |
714 | return -ENODATA; | |
48fb0d13 | 715 | |
416e8419 | 716 | if (type > m->containers[m->n_containers].max_attribute) |
48fb0d13 | 717 | return -ENODATA; |
44caa5e7 | 718 | |
f663aeb8 TG |
719 | attribute = &m->containers[m->n_containers].attributes[type]; |
720 | ||
f1dd72c2 | 721 | if (attribute->offset == 0) |
44caa5e7 SS |
722 | return -ENODATA; |
723 | ||
f663aeb8 | 724 | rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); |
44caa5e7 | 725 | |
3b4e3ebb YW |
726 | if (ret_data) |
727 | *ret_data = RTA_DATA(rta); | |
f66eeb6b | 728 | |
3b4e3ebb YW |
729 | if (ret_net_byteorder) |
730 | *ret_net_byteorder = attribute->net_byteorder; | |
4c641e99 | 731 | |
f66eeb6b | 732 | return RTA_PAYLOAD(rta); |
44caa5e7 SS |
733 | } |
734 | ||
926062f0 SS |
735 | int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data) { |
736 | void *attr_data; | |
737 | int r; | |
738 | ||
739 | assert_return(m, -EINVAL); | |
740 | ||
741 | r = netlink_message_read_internal(m, type, &attr_data, NULL); | |
742 | if (r < 0) | |
743 | return r; | |
744 | ||
745 | if ((size_t) r < size) | |
746 | return -EIO; | |
747 | ||
748 | if (data) | |
749 | memcpy(data, attr_data, size); | |
750 | ||
5dc3dbe8 | 751 | return r; |
926062f0 SS |
752 | } |
753 | ||
de52a83c | 754 | int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) { |
52888279 | 755 | void *attr_data; |
de52a83c YW |
756 | int r; |
757 | ||
758 | assert_return(m, -EINVAL); | |
759 | ||
760 | r = netlink_message_read_internal(m, type, &attr_data, NULL); | |
761 | if (r < 0) | |
762 | return r; | |
763 | ||
764 | if (ret_data) { | |
52888279 YW |
765 | void *data; |
766 | ||
de52a83c | 767 | data = memdup(attr_data, r); |
a1d2ae06 YW |
768 | if (!data) |
769 | return -ENOMEM; | |
770 | ||
771 | *ret_data = data; | |
772 | } | |
773 | ||
774 | if (ret_size) | |
775 | *ret_size = r; | |
776 | ||
777 | return r; | |
778 | } | |
779 | ||
780 | int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) { | |
781 | void *attr_data; | |
782 | int r; | |
783 | ||
784 | assert_return(m, -EINVAL); | |
785 | ||
786 | r = netlink_message_read_internal(m, type, &attr_data, NULL); | |
787 | if (r < 0) | |
788 | return r; | |
789 | ||
790 | if (ret_data) { | |
791 | void *data; | |
792 | ||
793 | data = memdup_suffix0(attr_data, r); | |
de52a83c YW |
794 | if (!data) |
795 | return -ENOMEM; | |
796 | ||
797 | *ret_data = data; | |
798 | } | |
799 | ||
800 | if (ret_size) | |
801 | *ret_size = r; | |
802 | ||
803 | return r; | |
804 | } | |
805 | ||
59d4103f YW |
806 | int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) { |
807 | void *attr_data; | |
59d4103f YW |
808 | int r; |
809 | ||
810 | assert_return(m, -EINVAL); | |
811 | ||
812 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING); | |
813 | if (r < 0) | |
814 | return r; | |
815 | ||
816 | r = netlink_message_read_internal(m, type, &attr_data, NULL); | |
817 | if (r < 0) | |
818 | return r; | |
819 | ||
820 | if (data) { | |
52888279 YW |
821 | char *str; |
822 | ||
59d4103f YW |
823 | str = strndup(attr_data, r); |
824 | if (!str) | |
825 | return -ENOMEM; | |
826 | ||
827 | *data = str; | |
828 | } | |
829 | ||
830 | return 0; | |
831 | } | |
832 | ||
1c4baffc | 833 | int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { |
44caa5e7 | 834 | void *attr_data; |
52888279 | 835 | int r; |
44caa5e7 | 836 | |
73ae2b7d TG |
837 | assert_return(m, -EINVAL); |
838 | ||
6c14ad61 | 839 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING); |
d8e538ec TG |
840 | if (r < 0) |
841 | return r; | |
44caa5e7 | 842 | |
4c641e99 | 843 | r = netlink_message_read_internal(m, type, &attr_data, NULL); |
f66eeb6b | 844 | if (r < 0) |
44caa5e7 | 845 | return r; |
52888279 YW |
846 | |
847 | if (strnlen(attr_data, r) >= (size_t) r) | |
f66eeb6b | 848 | return -EIO; |
44caa5e7 | 849 | |
73ae2b7d TG |
850 | if (data) |
851 | *data = (const char *) attr_data; | |
44caa5e7 SS |
852 | |
853 | return 0; | |
854 | } | |
855 | ||
1c4baffc | 856 | int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { |
44caa5e7 | 857 | void *attr_data; |
52888279 | 858 | int r; |
44caa5e7 | 859 | |
73ae2b7d TG |
860 | assert_return(m, -EINVAL); |
861 | ||
6c14ad61 | 862 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); |
d8e538ec TG |
863 | if (r < 0) |
864 | return r; | |
44caa5e7 | 865 | |
4c641e99 | 866 | r = netlink_message_read_internal(m, type, &attr_data, NULL); |
f66eeb6b | 867 | if (r < 0) |
44caa5e7 | 868 | return r; |
52888279 YW |
869 | |
870 | if ((size_t) r < sizeof(uint8_t)) | |
f66eeb6b | 871 | return -EIO; |
44caa5e7 | 872 | |
73ae2b7d TG |
873 | if (data) |
874 | *data = *(uint8_t *) attr_data; | |
44caa5e7 SS |
875 | |
876 | return 0; | |
877 | } | |
878 | ||
1c4baffc | 879 | int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) { |
44caa5e7 | 880 | void *attr_data; |
4c641e99 TG |
881 | bool net_byteorder; |
882 | int r; | |
44caa5e7 | 883 | |
73ae2b7d TG |
884 | assert_return(m, -EINVAL); |
885 | ||
6c14ad61 | 886 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); |
d8e538ec TG |
887 | if (r < 0) |
888 | return r; | |
44caa5e7 | 889 | |
4c641e99 | 890 | r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); |
f66eeb6b | 891 | if (r < 0) |
44caa5e7 | 892 | return r; |
52888279 YW |
893 | |
894 | if ((size_t) r < sizeof(uint16_t)) | |
f66eeb6b | 895 | return -EIO; |
44caa5e7 | 896 | |
4c641e99 TG |
897 | if (data) { |
898 | if (net_byteorder) | |
899 | *data = be16toh(*(uint16_t *) attr_data); | |
900 | else | |
901 | *data = *(uint16_t *) attr_data; | |
902 | } | |
44caa5e7 SS |
903 | |
904 | return 0; | |
905 | } | |
906 | ||
1c4baffc | 907 | int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) { |
44caa5e7 | 908 | void *attr_data; |
4c641e99 TG |
909 | bool net_byteorder; |
910 | int r; | |
44caa5e7 | 911 | |
73ae2b7d TG |
912 | assert_return(m, -EINVAL); |
913 | ||
6c14ad61 | 914 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); |
d8e538ec TG |
915 | if (r < 0) |
916 | return r; | |
44caa5e7 | 917 | |
4c641e99 | 918 | r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); |
f66eeb6b | 919 | if (r < 0) |
44caa5e7 | 920 | return r; |
52888279 YW |
921 | |
922 | if ((size_t) r < sizeof(uint32_t)) | |
f66eeb6b | 923 | return -EIO; |
44caa5e7 | 924 | |
4c641e99 TG |
925 | if (data) { |
926 | if (net_byteorder) | |
927 | *data = be32toh(*(uint32_t *) attr_data); | |
928 | else | |
929 | *data = *(uint32_t *) attr_data; | |
930 | } | |
44caa5e7 SS |
931 | |
932 | return 0; | |
933 | } | |
934 | ||
1c4baffc | 935 | int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { |
4e9e7f18 | 936 | void *attr_data; |
52888279 | 937 | int r; |
4e9e7f18 | 938 | |
73ae2b7d TG |
939 | assert_return(m, -EINVAL); |
940 | ||
6c14ad61 | 941 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); |
d8e538ec TG |
942 | if (r < 0) |
943 | return r; | |
4e9e7f18 | 944 | |
4c641e99 | 945 | r = netlink_message_read_internal(m, type, &attr_data, NULL); |
f66eeb6b | 946 | if (r < 0) |
4e9e7f18 | 947 | return r; |
52888279 YW |
948 | |
949 | if ((size_t) r < sizeof(struct ether_addr)) | |
f66eeb6b | 950 | return -EIO; |
4e9e7f18 | 951 | |
73ae2b7d TG |
952 | if (data) |
953 | memcpy(data, attr_data, sizeof(struct ether_addr)); | |
4e9e7f18 SS |
954 | |
955 | return 0; | |
956 | } | |
957 | ||
ca2b7cd8 | 958 | int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) { |
4fc8a29a | 959 | void *attr_data; |
52888279 | 960 | int r; |
4fc8a29a TR |
961 | |
962 | assert_return(m, -EINVAL); | |
963 | ||
964 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); | |
965 | if (r < 0) | |
966 | return r; | |
967 | ||
968 | r = netlink_message_read_internal(m, type, &attr_data, NULL); | |
969 | if (r < 0) | |
970 | return r; | |
52888279 YW |
971 | |
972 | if (r > HW_ADDR_MAX_SIZE) | |
4fc8a29a TR |
973 | return -EIO; |
974 | ||
975 | if (data) { | |
ca2b7cd8 | 976 | memcpy(data->bytes, attr_data, r); |
4fc8a29a TR |
977 | data->length = r; |
978 | } | |
979 | ||
980 | return 0; | |
981 | } | |
982 | ||
1c4baffc | 983 | int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { |
aba496a5 | 984 | void *attr_data; |
52888279 | 985 | int r; |
aba496a5 | 986 | |
73ae2b7d TG |
987 | assert_return(m, -EINVAL); |
988 | ||
6c14ad61 | 989 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); |
aba496a5 UTL |
990 | if (r < 0) |
991 | return r; | |
992 | ||
4c641e99 | 993 | r = netlink_message_read_internal(m, type, &attr_data, NULL); |
aba496a5 UTL |
994 | if (r < 0) |
995 | return r; | |
52888279 YW |
996 | |
997 | if ((size_t) r < sizeof(struct ifa_cacheinfo)) | |
aba496a5 UTL |
998 | return -EIO; |
999 | ||
73ae2b7d TG |
1000 | if (info) |
1001 | memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); | |
aba496a5 UTL |
1002 | |
1003 | return 0; | |
1004 | } | |
1005 | ||
f29b6b37 | 1006 | int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) { |
4e9e7f18 | 1007 | void *attr_data; |
f29b6b37 | 1008 | int r; |
4e9e7f18 | 1009 | |
73ae2b7d | 1010 | assert_return(m, -EINVAL); |
f29b6b37 | 1011 | assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL); |
73ae2b7d | 1012 | |
6c14ad61 | 1013 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); |
d8e538ec TG |
1014 | if (r < 0) |
1015 | return r; | |
4e9e7f18 | 1016 | |
4c641e99 | 1017 | r = netlink_message_read_internal(m, type, &attr_data, NULL); |
f66eeb6b | 1018 | if (r < 0) |
4e9e7f18 | 1019 | return r; |
52888279 YW |
1020 | |
1021 | if ((size_t) r < FAMILY_ADDRESS_SIZE(family)) | |
f66eeb6b | 1022 | return -EIO; |
4e9e7f18 | 1023 | |
73ae2b7d | 1024 | if (data) |
f29b6b37 | 1025 | memcpy(data, attr_data, FAMILY_ADDRESS_SIZE(family)); |
4e9e7f18 SS |
1026 | |
1027 | return 0; | |
1028 | } | |
1029 | ||
f29b6b37 YW |
1030 | int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { |
1031 | union in_addr_union u; | |
4e9e7f18 | 1032 | int r; |
4e9e7f18 | 1033 | |
f29b6b37 YW |
1034 | r = netlink_message_read_in_addr_union(m, type, AF_INET, &u); |
1035 | if (r >= 0 && data) | |
1036 | *data = u.in; | |
73ae2b7d | 1037 | |
f29b6b37 YW |
1038 | return r; |
1039 | } | |
4e9e7f18 | 1040 | |
f29b6b37 YW |
1041 | int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { |
1042 | union in_addr_union u; | |
1043 | int r; | |
4e9e7f18 | 1044 | |
f29b6b37 YW |
1045 | r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u); |
1046 | if (r >= 0 && data) | |
1047 | *data = u.in6; | |
4e9e7f18 | 1048 | |
f29b6b37 | 1049 | return r; |
4e9e7f18 SS |
1050 | } |
1051 | ||
3b4e3ebb YW |
1052 | int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type) { |
1053 | void *attr_data; | |
1054 | int r; | |
1055 | ||
1056 | assert_return(m, -EINVAL); | |
1057 | ||
1058 | /* This returns 1 when the flag is set, 0 when not set, negative errno on error. */ | |
1059 | ||
1060 | r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_FLAG); | |
1061 | if (r < 0) | |
1062 | return r; | |
1063 | ||
1064 | r = netlink_message_read_internal(m, type, &attr_data, NULL); | |
1065 | if (r == -ENODATA) | |
1066 | return 0; | |
1067 | if (r < 0) | |
1068 | return r; | |
1069 | ||
1070 | return 1; | |
1071 | } | |
1072 | ||
8f3c1859 YW |
1073 | int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) { |
1074 | _cleanup_strv_free_ char **s = NULL; | |
1075 | const NLTypeSystem *type_system; | |
1076 | const NLType *nl_type; | |
1077 | struct rtattr *rta; | |
1078 | void *container; | |
f501c251 | 1079 | size_t rt_len; |
8f3c1859 YW |
1080 | int r; |
1081 | ||
1082 | assert_return(m, -EINVAL); | |
409856d3 | 1083 | assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL); |
8f3c1859 | 1084 | |
98be4292 YW |
1085 | nl_type = type_system_get_type( |
1086 | m->containers[m->n_containers].type_system, | |
1087 | container_type); | |
1088 | if (!nl_type) | |
1089 | return -EOPNOTSUPP; | |
8f3c1859 YW |
1090 | |
1091 | if (type_get_type(nl_type) != NETLINK_TYPE_NESTED) | |
1092 | return -EINVAL; | |
1093 | ||
98be4292 YW |
1094 | type_system = type_system_get_type_system( |
1095 | m->containers[m->n_containers].type_system, | |
1096 | container_type); | |
1097 | if (!type_system) | |
1098 | return -EOPNOTSUPP; | |
8f3c1859 | 1099 | |
98be4292 YW |
1100 | nl_type = type_system_get_type(type_system, type_id); |
1101 | if (!nl_type) | |
1102 | return -EOPNOTSUPP; | |
8f3c1859 YW |
1103 | |
1104 | if (type_get_type(nl_type) != NETLINK_TYPE_STRING) | |
1105 | return -EINVAL; | |
1106 | ||
1107 | r = netlink_message_read_internal(m, container_type, &container, NULL); | |
1108 | if (r < 0) | |
1109 | return r; | |
1110 | ||
f501c251 | 1111 | rt_len = (size_t) r; |
8f3c1859 YW |
1112 | rta = container; |
1113 | ||
f501c251 YW |
1114 | /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and |
1115 | * LGTM.com analysis does not like the type difference. Hence, here we | |
1116 | * introduce an unsigned short variable as a workaround. */ | |
1117 | unsigned short len = rt_len; | |
1118 | for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { | |
8f3c1859 YW |
1119 | unsigned short type; |
1120 | ||
1121 | type = RTA_TYPE(rta); | |
1122 | if (type != type_id) | |
1123 | continue; | |
1124 | ||
1125 | r = strv_extend(&s, RTA_DATA(rta)); | |
1126 | if (r < 0) | |
1127 | return r; | |
1128 | } | |
1129 | ||
1130 | *ret = TAKE_PTR(s); | |
1131 | return 0; | |
1132 | } | |
1133 | ||
416e8419 YW |
1134 | static int netlink_container_parse( |
1135 | sd_netlink_message *m, | |
1136 | struct netlink_container *container, | |
1137 | struct rtattr *rta, | |
1138 | size_t rt_len) { | |
1139 | ||
f663aeb8 | 1140 | _cleanup_free_ struct netlink_attribute *attributes = NULL; |
416e8419 | 1141 | uint16_t max_attr = 0; |
4203fc8b | 1142 | |
f501c251 YW |
1143 | /* RTA_OK() macro compares with rta->rt_len, which is unsigned short, and |
1144 | * LGTM.com analysis does not like the type difference. Hence, here we | |
1145 | * introduce an unsigned short variable as a workaround. */ | |
1146 | unsigned short len = rt_len; | |
1147 | for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { | |
416e8419 | 1148 | uint16_t attr; |
f663aeb8 | 1149 | |
416e8419 YW |
1150 | attr = RTA_TYPE(rta); |
1151 | max_attr = MAX(max_attr, attr); | |
4203fc8b | 1152 | |
416e8419 | 1153 | if (!GREEDY_REALLOC0(attributes, (size_t) max_attr + 1)) |
5fc5e2f5 | 1154 | return -ENOMEM; |
4203fc8b | 1155 | |
416e8419 | 1156 | if (attributes[attr].offset != 0) |
409856d3 | 1157 | log_debug("sd-netlink: message parse - overwriting repeated attribute"); |
4203fc8b | 1158 | |
416e8419 YW |
1159 | attributes[attr].offset = (uint8_t *) rta - (uint8_t *) m->hdr; |
1160 | attributes[attr].nested = RTA_FLAGS(rta) & NLA_F_NESTED; | |
1161 | attributes[attr].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; | |
4203fc8b TG |
1162 | } |
1163 | ||
ae2a15bc | 1164 | container->attributes = TAKE_PTR(attributes); |
416e8419 | 1165 | container->max_attribute = max_attr; |
4203fc8b TG |
1166 | |
1167 | return 0; | |
1168 | } | |
1169 | ||
817d1cd8 | 1170 | int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) { |
d8e538ec TG |
1171 | const NLType *nl_type; |
1172 | const NLTypeSystem *type_system; | |
3dd215e0 | 1173 | void *container; |
817d1cd8 | 1174 | uint16_t type; |
d8e538ec TG |
1175 | size_t size; |
1176 | int r; | |
3dd215e0 TG |
1177 | |
1178 | assert_return(m, -EINVAL); | |
409856d3 | 1179 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL); |
3dd215e0 | 1180 | |
98be4292 YW |
1181 | nl_type = type_system_get_type( |
1182 | m->containers[m->n_containers].type_system, | |
1183 | type_id); | |
1184 | if (!nl_type) | |
1185 | return -EOPNOTSUPP; | |
3dd215e0 | 1186 | |
817d1cd8 DH |
1187 | type = type_get_type(nl_type); |
1188 | ||
1189 | if (type == NETLINK_TYPE_NESTED) { | |
98be4292 YW |
1190 | type_system = type_system_get_type_system( |
1191 | m->containers[m->n_containers].type_system, | |
1192 | type_id); | |
1193 | if (!type_system) | |
1194 | return -EOPNOTSUPP; | |
817d1cd8 | 1195 | } else if (type == NETLINK_TYPE_UNION) { |
d8e538ec | 1196 | const NLTypeSystemUnion *type_system_union; |
d8e538ec | 1197 | |
98be4292 YW |
1198 | type_system_union = type_system_get_type_system_union( |
1199 | m->containers[m->n_containers].type_system, | |
1200 | type_id); | |
1201 | if (!type_system_union) | |
1202 | return -EOPNOTSUPP; | |
d8e538ec | 1203 | |
b019c545 YW |
1204 | switch (type_system_union_get_match_type(type_system_union)) { |
1205 | case NL_MATCH_SIBLING: { | |
4af7b60d TG |
1206 | const char *key; |
1207 | ||
b019c545 YW |
1208 | r = sd_netlink_message_read_string( |
1209 | m, | |
1210 | type_system_union_get_match_attribute(type_system_union), | |
1211 | &key); | |
4af7b60d TG |
1212 | if (r < 0) |
1213 | return r; | |
1214 | ||
98be4292 | 1215 | type_system = type_system_union_get_type_system_by_string( |
ea073c8f | 1216 | type_system_union, |
ea073c8f | 1217 | key); |
98be4292 YW |
1218 | if (!type_system) |
1219 | return -EOPNOTSUPP; | |
4af7b60d TG |
1220 | |
1221 | break; | |
1222 | } | |
b019c545 | 1223 | case NL_MATCH_PROTOCOL: { |
4af7b60d TG |
1224 | int family; |
1225 | ||
89489ef7 | 1226 | r = sd_rtnl_message_get_family(m, &family); |
4af7b60d TG |
1227 | if (r < 0) |
1228 | return r; | |
1229 | ||
98be4292 | 1230 | type_system = type_system_union_get_type_system_by_protocol( |
ea073c8f | 1231 | type_system_union, |
ea073c8f | 1232 | family); |
98be4292 YW |
1233 | if (!type_system) |
1234 | return -EOPNOTSUPP; | |
4af7b60d TG |
1235 | |
1236 | break; | |
1237 | } | |
1238 | default: | |
04499a70 | 1239 | assert_not_reached(); |
4af7b60d | 1240 | } |
d8e538ec TG |
1241 | } else |
1242 | return -EINVAL; | |
1243 | ||
4c641e99 | 1244 | r = netlink_message_read_internal(m, type_id, &container, NULL); |
3dd215e0 TG |
1245 | if (r < 0) |
1246 | return r; | |
620fd5d4 | 1247 | |
6d185cff | 1248 | size = (size_t) r; |
313cefa1 | 1249 | m->n_containers++; |
3dd215e0 | 1250 | |
f663aeb8 TG |
1251 | r = netlink_container_parse(m, |
1252 | &m->containers[m->n_containers], | |
f663aeb8 TG |
1253 | container, |
1254 | size); | |
d8e538ec | 1255 | if (r < 0) { |
313cefa1 | 1256 | m->n_containers--; |
3dd215e0 | 1257 | return r; |
d8e538ec | 1258 | } |
3dd215e0 | 1259 | |
f663aeb8 | 1260 | m->containers[m->n_containers].type_system = type_system; |
3dd215e0 TG |
1261 | |
1262 | return 0; | |
1263 | } | |
1264 | ||
5fc5e2f5 YW |
1265 | int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) { |
1266 | void *container; | |
1267 | size_t size; | |
1268 | int r; | |
1269 | ||
1270 | assert_return(m, -EINVAL); | |
409856d3 | 1271 | assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL); |
5fc5e2f5 YW |
1272 | |
1273 | r = netlink_message_read_internal(m, type_id, &container, NULL); | |
1274 | if (r < 0) | |
1275 | return r; | |
1276 | ||
1277 | size = (size_t) r; | |
5fc5e2f5 YW |
1278 | m->n_containers++; |
1279 | ||
1280 | r = netlink_container_parse(m, | |
1281 | &m->containers[m->n_containers], | |
1282 | container, | |
1283 | size); | |
1284 | if (r < 0) { | |
1285 | m->n_containers--; | |
1286 | return r; | |
1287 | } | |
1288 | ||
1289 | m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system; | |
1290 | ||
1291 | return 0; | |
1292 | } | |
1293 | ||
1c4baffc | 1294 | int sd_netlink_message_exit_container(sd_netlink_message *m) { |
e5c4350b TG |
1295 | assert_return(m, -EINVAL); |
1296 | assert_return(m->sealed, -EINVAL); | |
1297 | assert_return(m->n_containers > 0, -EINVAL); | |
1298 | ||
a1e58e8e | 1299 | m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); |
416e8419 | 1300 | m->containers[m->n_containers].max_attribute = 0; |
f663aeb8 | 1301 | m->containers[m->n_containers].type_system = NULL; |
3dd215e0 | 1302 | |
313cefa1 | 1303 | m->n_containers--; |
e5c4350b TG |
1304 | |
1305 | return 0; | |
1306 | } | |
1307 | ||
416e8419 YW |
1308 | int sd_netlink_message_get_max_attribute(sd_netlink_message *m, uint16_t *ret) { |
1309 | assert_return(m, -EINVAL); | |
1310 | assert_return(m->sealed, -EINVAL); | |
1311 | assert_return(ret, -EINVAL); | |
1312 | ||
1313 | *ret = m->containers[m->n_containers].max_attribute; | |
1314 | return 0; | |
1315 | } | |
1316 | ||
409856d3 | 1317 | uint32_t message_get_serial(sd_netlink_message *m) { |
65f568bb | 1318 | assert(m); |
9d0db178 | 1319 | assert(m->hdr); |
65f568bb TG |
1320 | |
1321 | return m->hdr->nlmsg_seq; | |
1322 | } | |
1323 | ||
041ea9f9 | 1324 | int sd_netlink_message_is_error(sd_netlink_message *m) { |
45af44d4 TG |
1325 | assert_return(m, 0); |
1326 | assert_return(m->hdr, 0); | |
1327 | ||
1328 | return m->hdr->nlmsg_type == NLMSG_ERROR; | |
1329 | } | |
1330 | ||
041ea9f9 | 1331 | int sd_netlink_message_get_errno(sd_netlink_message *m) { |
65f568bb TG |
1332 | struct nlmsgerr *err; |
1333 | ||
e16bcf98 | 1334 | assert_return(m, -EINVAL); |
9d0db178 | 1335 | assert_return(m->hdr, -EINVAL); |
65f568bb | 1336 | |
1c4baffc | 1337 | if (!sd_netlink_message_is_error(m)) |
65f568bb TG |
1338 | return 0; |
1339 | ||
1340 | err = NLMSG_DATA(m->hdr); | |
1341 | ||
1342 | return err->error; | |
1343 | } | |
1344 | ||
e4a1e68d YW |
1345 | static int netlink_message_parse_error(sd_netlink_message *m) { |
1346 | struct nlmsgerr *err = NLMSG_DATA(m->hdr); | |
1347 | size_t hlen = sizeof(struct nlmsgerr); | |
1348 | ||
1349 | /* no TLVs, nothing to do here */ | |
1350 | if (!(m->hdr->nlmsg_flags & NLM_F_ACK_TLVS)) | |
1351 | return 0; | |
1352 | ||
1353 | /* if NLM_F_CAPPED is set then the inner err msg was capped */ | |
1354 | if (!(m->hdr->nlmsg_flags & NLM_F_CAPPED)) | |
1355 | hlen += err->msg.nlmsg_len - sizeof(struct nlmsghdr); | |
1356 | ||
1357 | if (m->hdr->nlmsg_len <= NLMSG_SPACE(hlen)) | |
1358 | return 0; | |
1359 | ||
1360 | return netlink_container_parse(m, | |
1361 | &m->containers[m->n_containers], | |
1362 | (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + hlen), | |
1363 | NLMSG_PAYLOAD(m->hdr, hlen)); | |
1364 | } | |
1365 | ||
aee6309b | 1366 | int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) { |
817d1cd8 | 1367 | size_t size; |
3dd215e0 | 1368 | int r; |
0fc7531b TG |
1369 | |
1370 | assert_return(m, -EINVAL); | |
aee6309b | 1371 | assert_return(nl, -EINVAL); |
0fc7531b | 1372 | |
3dd215e0 | 1373 | /* don't allow appending to message once parsed */ |
c737abd3 | 1374 | message_seal(m); |
3dd215e0 | 1375 | |
c7209bcf | 1376 | for (unsigned i = 1; i <= m->n_containers; i++) |
a1e58e8e | 1377 | m->containers[i].attributes = mfree(m->containers[i].attributes); |
3dd215e0 TG |
1378 | |
1379 | m->n_containers = 0; | |
1380 | ||
ece174c5 | 1381 | if (m->containers[0].attributes) |
3dd215e0 TG |
1382 | /* top-level attributes have already been parsed */ |
1383 | return 0; | |
3dd215e0 | 1384 | |
d8e538ec TG |
1385 | assert(m->hdr); |
1386 | ||
56fdc16d YW |
1387 | r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type, |
1388 | &m->containers[0].type_system, &size); | |
d8e538ec TG |
1389 | if (r < 0) |
1390 | return r; | |
1391 | ||
56fdc16d YW |
1392 | if (sd_netlink_message_is_error(m)) |
1393 | return netlink_message_parse_error(m); | |
d8e538ec | 1394 | |
56fdc16d YW |
1395 | return netlink_container_parse(m, |
1396 | &m->containers[0], | |
1397 | (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), | |
1398 | NLMSG_PAYLOAD(m->hdr, size)); | |
0fc7531b | 1399 | } |
3dd215e0 | 1400 | |
409856d3 | 1401 | void message_seal(sd_netlink_message *m) { |
3dd215e0 | 1402 | assert(m); |
3dd215e0 TG |
1403 | |
1404 | m->sealed = true; | |
1405 | } | |
1403f45a | 1406 | |
1c4baffc | 1407 | sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) { |
1403f45a TG |
1408 | assert_return(m, NULL); |
1409 | ||
1410 | return m->next; | |
1411 | } |