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