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