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