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