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