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