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