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