]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-util.c
Two fixlets for coverage test (#38183)
[thirdparty/systemd.git] / src / libsystemd / sd-netlink / netlink-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
d8921c6d 2
1c4baffc 3#include "sd-netlink.h"
d8921c6d 4
5cdf13c7 5#include "ether-addr-util.h"
84e10015 6#include "fd-util.h"
5cdf13c7 7#include "hashmap.h"
bd1ae178 8#include "iovec-util.h"
93a1f792 9#include "log.h"
6497a8aa 10#include "memory-util.h"
1c4baffc 11#include "netlink-internal.h"
cf0fbc49 12#include "netlink-util.h"
f6e49154 13#include "parse-util.h"
84e10015 14#include "process-util.h"
5cdf13c7
DDM
15#include "socket-util.h"
16#include "string-util.h"
a5053a15 17#include "strv.h"
d8921c6d 18
4e235561
YW
19static int parse_newlink_message(
20 sd_netlink_message *message,
21 char **ret_name,
22 char ***ret_altnames) {
23
24 _cleanup_strv_free_ char **altnames = NULL;
25 int r, ifindex;
26
27 assert(message);
28
29 uint16_t type;
30 r = sd_netlink_message_get_type(message, &type);
31 if (r < 0)
32 return r;
33 if (type != RTM_NEWLINK)
34 return -EPROTO;
35
36 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
37 if (r < 0)
38 return r;
39 if (ifindex <= 0)
40 return -EPROTO;
41
42 if (ret_altnames) {
43 r = sd_netlink_message_read_strv(message, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
44 if (r < 0 && r != -ENODATA)
45 return r;
46 }
47
48 if (ret_name) {
49 r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, ret_name);
50 if (r < 0)
51 return r;
52 }
53
54 if (ret_altnames)
55 *ret_altnames = TAKE_PTR(altnames);
56
57 return ifindex;
58}
59
4e235561
YW
60int rtnl_resolve_ifname_full(
61 sd_netlink **rtnl,
62 ResolveInterfaceNameFlag flags,
63 const char *name,
64 char **ret_name,
65 char ***ret_altnames) {
66
67 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
68 int r;
69
70 assert(name);
71 assert(flags > 0);
72
73 /* This is similar to if_nametoindex(), but also resolves alternative names and decimal formatted
74 * ifindex too. Returns ifindex, and optionally provides the main interface name and alternative
d7306348 75 * names. */
4e235561
YW
76
77 if (!rtnl)
78 rtnl = &our_rtnl;
79 if (!*rtnl) {
80 r = sd_netlink_open(rtnl);
81 if (r < 0)
82 return r;
83 }
84
85 /* First, use IFLA_IFNAME */
86 if (FLAGS_SET(flags, RESOLVE_IFNAME_MAIN) && ifname_valid(name)) {
87 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
88
89 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
90 if (r < 0)
91 return r;
92
93 r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
94 if (r < 0)
95 return r;
96
97 r = sd_netlink_call(*rtnl, message, 0, &reply);
98 if (r >= 0)
99 return parse_newlink_message(reply, ret_name, ret_altnames);
100 if (r != -ENODEV)
101 return r;
102 }
103
104 /* Next, try IFLA_ALT_IFNAME */
105 if (FLAGS_SET(flags, RESOLVE_IFNAME_ALTERNATIVE) &&
106 ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE)) {
107 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
108
109 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
110 if (r < 0)
111 return r;
112
113 r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
114 if (r < 0)
115 return r;
116
117 r = sd_netlink_call(*rtnl, message, 0, &reply);
118 if (r >= 0)
119 return parse_newlink_message(reply, ret_name, ret_altnames);
120 /* The kernels older than 76c9ac0ee878f6693d398d3a95ccaf85e1f597a6 (v5.5) return -EINVAL. */
121 if (!IN_SET(r, -ENODEV, -EINVAL))
122 return r;
123 }
124
125 /* Finally, assume the string is a decimal formatted ifindex. */
126 if (FLAGS_SET(flags, RESOLVE_IFNAME_NUMERIC)) {
127 int ifindex;
128
129 ifindex = parse_ifindex(name);
130 if (ifindex <= 0)
131 return -ENODEV;
132
133 return rtnl_get_ifname_full(rtnl, ifindex, ret_name, ret_altnames);
134 }
135
136 return -ENODEV;
137}
138
93a1f792
DDM
139int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
140 int r;
141
142 assert(name);
143
144 r = rtnl_resolve_interface(rtnl, name);
145 if (r < 0)
146 return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
147 return r;
148}
149
472ad6af 150static int set_link_name(sd_netlink *rtnl, int ifindex, const char *name) {
4afd3348 151 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
d8921c6d
TG
152 int r;
153
154 assert(rtnl);
155 assert(ifindex > 0);
3e137a1b 156 assert(name);
d8921c6d 157
81824455 158 /* Assign the requested name. */
6e931bc5 159
472ad6af 160 r = sd_rtnl_message_new_link(rtnl, &message, RTM_SETLINK, ifindex);
81824455
YW
161 if (r < 0)
162 return r;
163
164 r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
165 if (r < 0)
166 return r;
167
472ad6af 168 return sd_netlink_call(rtnl, message, 0, NULL);
81824455
YW
169}
170
6e931bc5
YW
171int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name) {
172 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
173 int r, ifindex;
174
175 assert(orig_name);
176 assert(new_name);
177
178 /* This does not check alternative names. Callers must check the requested name is not used as an
179 * alternative name. */
180
181 if (streq(orig_name, new_name))
182 return 0;
183
184 if (!ifname_valid(new_name))
185 return -EINVAL;
186
187 if (!rtnl)
188 rtnl = &our_rtnl;
189 if (!*rtnl) {
190 r = sd_netlink_open(rtnl);
191 if (r < 0)
192 return r;
193 }
194
195 ifindex = rtnl_resolve_ifname(rtnl, orig_name);
196 if (ifindex < 0)
197 return ifindex;
198
472ad6af 199 return set_link_name(*rtnl, ifindex, new_name);
6e931bc5
YW
200}
201
81824455
YW
202int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) {
203 _cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL;
204 bool altname_deleted = false;
205 int r;
206
81824455
YW
207 assert(ifindex > 0);
208
209 if (isempty(name) && strv_isempty(alternative_names))
210 return 0;
211
212 if (name && !ifname_valid(name))
4d643099
YW
213 return -EINVAL;
214
472ad6af
YW
215 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
216 if (!rtnl)
217 rtnl = &our_rtnl;
218
81824455
YW
219 /* If the requested name is already assigned as an alternative name, then first drop it. */
220 r = rtnl_get_link_alternative_names(rtnl, ifindex, &original_altnames);
434a3483
YW
221 if (r < 0)
222 log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m",
223 ifindex);
224
81824455
YW
225 if (name) {
226 if (strv_contains(original_altnames, name)) {
227 r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
228 if (r < 0)
229 return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m",
230 name, ifindex);
231
232 altname_deleted = true;
233 }
4d600667 234
472ad6af 235 r = set_link_name(*rtnl, ifindex, name);
81824455
YW
236 if (r < 0)
237 goto fail;
4c83d994
TG
238 }
239
81824455
YW
240 /* Filter out already assigned names from requested alternative names. Also, dedup the request. */
241 STRV_FOREACH(a, alternative_names) {
242 if (streq_ptr(name, *a))
243 continue;
d8921c6d 244
81824455
YW
245 if (strv_contains(original_altnames, *a))
246 continue;
d8921c6d 247
81824455
YW
248 if (strv_contains(new_altnames, *a))
249 continue;
250
251 if (!ifname_valid_full(*a, IFNAME_VALID_ALTERNATIVE))
252 continue;
253
254 r = strv_extend(&new_altnames, *a);
255 if (r < 0)
256 return r;
257 }
258
259 strv_sort(new_altnames);
260
261 /* Finally, assign alternative names. */
262 r = rtnl_set_link_alternative_names(rtnl, ifindex, new_altnames);
263 if (r == -EEXIST) /* Already assigned to another interface? */
264 STRV_FOREACH(a, new_altnames) {
265 r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(*a));
266 if (r < 0)
267 log_debug_errno(r, "Failed to assign '%s' as an alternative name on network interface %i, ignoring: %m",
268 *a, ifindex);
269 }
270 else if (r < 0)
271 log_debug_errno(r, "Failed to assign alternative names on network interface %i, ignoring: %m", ifindex);
3e137a1b
TG
272
273 return 0;
4d600667
NR
274
275fail:
276 if (altname_deleted) {
277 int q = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
278 if (q < 0)
279 log_debug_errno(q, "Failed to restore '%s' as an alternative name on network interface %i, ignoring: %m",
280 name, ifindex);
281 }
282
283 return r;
3e137a1b
TG
284}
285
face9fcc
YW
286int rtnl_set_link_properties(
287 sd_netlink **rtnl,
288 int ifindex,
289 const char *alias,
9e2b7763 290 const struct hw_addr_data *hw_addr,
face9fcc
YW
291 uint32_t txqueues,
292 uint32_t rxqueues,
293 uint32_t txqueuelen,
294 uint32_t mtu,
295 uint32_t gso_max_size,
296 size_t gso_max_segments) {
472ad6af 297
3e137a1b
TG
298 int r;
299
3e137a1b
TG
300 assert(ifindex > 0);
301
9e2b7763
YW
302 if (!alias &&
303 (!hw_addr || hw_addr->length == 0) &&
304 txqueues == 0 &&
305 rxqueues == 0 &&
306 txqueuelen == UINT32_MAX &&
307 mtu == 0 &&
308 gso_max_size == 0 &&
309 gso_max_segments == 0)
3e137a1b
TG
310 return 0;
311
472ad6af
YW
312 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
313 if (!rtnl)
314 rtnl = &our_rtnl;
aedca892 315 if (!*rtnl) {
1c4baffc 316 r = sd_netlink_open(rtnl);
aedca892
TG
317 if (r < 0)
318 return r;
319 }
320
472ad6af 321 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
aedca892 322 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
3e137a1b
TG
323 if (r < 0)
324 return r;
d8921c6d 325
d2df0d0e 326 if (alias) {
1c4baffc 327 r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias);
d2df0d0e
TG
328 if (r < 0)
329 return r;
d2df0d0e
TG
330 }
331
9e2b7763
YW
332 if (hw_addr && hw_addr->length > 0) {
333 r = netlink_message_append_hw_addr(message, IFLA_ADDRESS, hw_addr);
d8921c6d
TG
334 if (r < 0)
335 return r;
d8921c6d
TG
336 }
337
face9fcc
YW
338 if (txqueues > 0) {
339 r = sd_netlink_message_append_u32(message, IFLA_NUM_TX_QUEUES, txqueues);
340 if (r < 0)
341 return r;
342 }
343
344 if (rxqueues > 0) {
345 r = sd_netlink_message_append_u32(message, IFLA_NUM_RX_QUEUES, rxqueues);
346 if (r < 0)
347 return r;
348 }
349
ef4a91a7
350 if (txqueuelen < UINT32_MAX) {
351 r = sd_netlink_message_append_u32(message, IFLA_TXQLEN, txqueuelen);
352 if (r < 0)
353 return r;
354 }
355
4e964aa0 356 if (mtu != 0) {
1c4baffc 357 r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu);
d8921c6d
TG
358 if (r < 0)
359 return r;
d8921c6d
TG
360 }
361
c97cad60
SS
362 if (gso_max_size > 0) {
363 r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SIZE, gso_max_size);
364 if (r < 0)
365 return r;
366 }
367
368 if (gso_max_segments > 0) {
369 r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SEGS, gso_max_segments);
370 if (r < 0)
371 return r;
372 }
373
1c4baffc 374 r = sd_netlink_call(*rtnl, message, 0, NULL);
aedca892
TG
375 if (r < 0)
376 return r;
d8921c6d
TG
377
378 return 0;
379}
3815f36f 380
f4f81a6b
ZJS
381static int rtnl_update_link_alternative_names(
382 sd_netlink **rtnl,
383 uint16_t nlmsg_type,
384 int ifindex,
385 char* const *alternative_names) {
386
a5053a15
YW
387 int r;
388
a5053a15 389 assert(ifindex > 0);
14982526 390 assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP));
a5053a15
YW
391
392 if (strv_isempty(alternative_names))
393 return 0;
394
472ad6af
YW
395 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
396 if (!rtnl)
397 rtnl = &our_rtnl;
a5053a15
YW
398 if (!*rtnl) {
399 r = sd_netlink_open(rtnl);
400 if (r < 0)
401 return r;
402 }
403
472ad6af 404 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
14982526 405 r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex);
a5053a15
YW
406 if (r < 0)
407 return r;
408
409 r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
410 if (r < 0)
411 return r;
412
f4f81a6b 413 r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, (const char**) alternative_names);
a5053a15
YW
414 if (r < 0)
415 return r;
416
417 r = sd_netlink_message_close_container(message);
418 if (r < 0)
419 return r;
420
421 r = sd_netlink_call(*rtnl, message, 0, NULL);
422 if (r < 0)
423 return r;
424
425 return 0;
426}
427
f4f81a6b 428int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
14982526
YW
429 return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names);
430}
431
f4f81a6b 432int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
14982526
YW
433 return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names);
434}
435
f4f81a6b
ZJS
436int rtnl_set_link_alternative_names_by_ifname(
437 sd_netlink **rtnl,
438 const char *ifname,
439 char* const *alternative_names) {
440
6b50cb5c
YW
441 int r;
442
443 assert(rtnl);
444 assert(ifname);
445
446 if (strv_isempty(alternative_names))
447 return 0;
448
472ad6af
YW
449 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
450 if (!rtnl)
451 rtnl = &our_rtnl;
6b50cb5c
YW
452 if (!*rtnl) {
453 r = sd_netlink_open(rtnl);
454 if (r < 0)
455 return r;
456 }
457
472ad6af 458 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
6b50cb5c
YW
459 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, 0);
460 if (r < 0)
461 return r;
462
463 r = sd_netlink_message_append_string(message, IFLA_IFNAME, ifname);
464 if (r < 0)
465 return r;
466
467 r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
468 if (r < 0)
469 return r;
470
f4f81a6b 471 r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, (const char**) alternative_names);
6b50cb5c
YW
472 if (r < 0)
473 return r;
474
475 r = sd_netlink_message_close_container(message);
476 if (r < 0)
477 return r;
478
479 r = sd_netlink_call(*rtnl, message, 0, NULL);
480 if (r < 0)
481 return r;
482
483 return 0;
484}
485
0e44a7c0 486int rtnl_get_link_info_full(
1de88f30
YW
487 sd_netlink **rtnl,
488 int ifindex,
0e44a7c0
YW
489 char **ret_name,
490 char ***ret_altnames,
1de88f30
YW
491 unsigned short *ret_iftype,
492 unsigned *ret_flags,
65022cd7 493 char **ret_kind,
1de88f30
YW
494 struct hw_addr_data *ret_hw_addr,
495 struct hw_addr_data *ret_permanent_hw_addr) {
496
ef62949a 497 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
0e44a7c0 498 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
1de88f30 499 struct hw_addr_data addr = HW_ADDR_NULL, perm_addr = HW_ADDR_NULL;
0e44a7c0
YW
500 _cleanup_free_ char *name = NULL, *kind = NULL;
501 _cleanup_strv_free_ char **altnames = NULL;
f25e642b
YW
502 unsigned short iftype;
503 unsigned flags;
ef62949a
YW
504 int r;
505
f25e642b
YW
506 assert(ifindex > 0);
507
0e44a7c0
YW
508 if (!rtnl)
509 rtnl = &our_rtnl;
ef62949a
YW
510 if (!*rtnl) {
511 r = sd_netlink_open(rtnl);
512 if (r < 0)
513 return r;
514 }
515
516 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
517 if (r < 0)
518 return r;
519
520 r = sd_netlink_call(*rtnl, message, 0, &reply);
521 if (r == -EINVAL)
522 return -ENODEV; /* The device does not exist */
523 if (r < 0)
524 return r;
525
0e44a7c0
YW
526 r = parse_newlink_message(reply, ret_name ? &name : NULL, ret_altnames ? &altnames : NULL);
527 if (r < 0)
528 return r;
529 if (r != ifindex)
530 return -EIO;
531
f25e642b
YW
532 if (ret_iftype) {
533 r = sd_rtnl_message_link_get_type(reply, &iftype);
534 if (r < 0)
535 return r;
536 }
537
538 if (ret_flags) {
539 r = sd_rtnl_message_link_get_flags(reply, &flags);
540 if (r < 0)
541 return r;
542 }
543
65022cd7
YW
544 if (ret_kind) {
545 r = sd_netlink_message_enter_container(reply, IFLA_LINKINFO);
546 if (r >= 0) {
547 r = sd_netlink_message_read_string_strdup(reply, IFLA_INFO_KIND, &kind);
548 if (r < 0 && r != -ENODATA)
549 return r;
550
551 r = sd_netlink_message_exit_container(reply);
552 if (r < 0)
553 return r;
554 }
555 }
556
1de88f30
YW
557 if (ret_hw_addr) {
558 r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &addr);
559 if (r < 0 && r != -ENODATA)
560 return r;
561 }
562
563 if (ret_permanent_hw_addr) {
564 r = netlink_message_read_hw_addr(reply, IFLA_PERM_ADDRESS, &perm_addr);
565 if (r < 0 && r != -ENODATA)
566 return r;
567 }
568
0e44a7c0
YW
569 if (ret_name)
570 *ret_name = TAKE_PTR(name);
571 if (ret_altnames)
572 *ret_altnames = TAKE_PTR(altnames);
f25e642b
YW
573 if (ret_iftype)
574 *ret_iftype = iftype;
575 if (ret_flags)
576 *ret_flags = flags;
65022cd7
YW
577 if (ret_kind)
578 *ret_kind = TAKE_PTR(kind);
1de88f30
YW
579 if (ret_hw_addr)
580 *ret_hw_addr = addr;
581 if (ret_permanent_hw_addr)
582 *ret_permanent_hw_addr = perm_addr;
0e44a7c0 583 return ifindex;
ef62949a
YW
584}
585
ee8c4568 586int rtnl_log_parse_error(int r) {
8d3d7072 587 return log_error_errno(r, "Failed to parse netlink message: %m");
ee8c4568
LP
588}
589
590int rtnl_log_create_error(int r) {
8d3d7072 591 return log_error_errno(r, "Failed to create netlink message: %m");
ee8c4568 592}
6497a8aa
YW
593
594void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) {
595 size_t padding_length;
14b6e6b6 596 uint8_t *padding;
6497a8aa
YW
597
598 assert(rta);
599 assert(!data || data_length > 0);
600
601 /* fill in the attribute */
602 rta->rta_type = type;
603 rta->rta_len = RTA_LENGTH(data_length);
604 if (data)
605 /* we don't deal with the case where the user lies about the type
606 * and gives us too little data (so don't do that)
607 */
608 padding = mempcpy(RTA_DATA(rta), data, data_length);
609
610 else
611 /* if no data was passed, make sure we still initialize the padding
612 note that we can have data_length > 0 (used by some containers) */
613 padding = RTA_DATA(rta);
614
615 /* make sure also the padding at the end of the message is initialized */
14b6e6b6 616 padding_length = (uint8_t *) rta + RTA_SPACE(data_length) - padding;
6497a8aa
YW
617 memzero(padding, padding_length);
618}
619
620int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) {
621 struct rtattr *new_rta, *sub_rta;
622 size_t message_length;
623
624 assert(rta);
625 assert(!data || data_length > 0);
626
627 /* get the new message size (with padding at the end) */
628 message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length);
629
630 /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
631 if (message_length > MIN(page_size(), 8192UL))
632 return -ENOBUFS;
633
634 /* realloc to fit the new attribute */
635 new_rta = realloc(*rta, message_length);
636 if (!new_rta)
637 return -ENOMEM;
638 *rta = new_rta;
639
640 /* get pointer to the attribute we are about to add */
641 sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len));
642
643 rtattr_append_attribute_internal(sub_rta, type, data, data_length);
644
645 /* update rta_len */
646 (*rta)->rta_len = message_length;
647
648 return 0;
649}
2fe1d557 650
84e10015
ZJS
651bool netlink_pid_changed(sd_netlink *nl) {
652 /* We don't support people creating an nl connection and
653 * keeping it around over a fork(). Let's complain. */
654 return ASSERT_PTR(nl)->original_pid != getpid_cached();
655}
656
657static int socket_open(int family) {
658 int fd;
659
660 fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
661 if (fd < 0)
662 return -errno;
663
664 return fd_move_above_stdio(fd);
665}
666
667int netlink_open_family(sd_netlink **ret, int family) {
254d1313 668 _cleanup_close_ int fd = -EBADF;
84e10015
ZJS
669 int r;
670
671 fd = socket_open(family);
672 if (fd < 0)
673 return fd;
674
675 r = sd_netlink_open_fd(ret, fd);
676 if (r < 0)
677 return r;
678 TAKE_FD(fd);
679
680 return 0;
681}
682
2b176edc
YW
683static bool serial_used(sd_netlink *nl, uint32_t serial) {
684 assert(nl);
685
686 return
687 hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(serial)) ||
688 hashmap_contains(nl->rqueue_by_serial, UINT32_TO_PTR(serial)) ||
689 hashmap_contains(nl->rqueue_partial_by_serial, UINT32_TO_PTR(serial));
690}
691
84e10015
ZJS
692void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m) {
693 uint32_t picked;
694
695 assert(nl);
696 assert(!netlink_pid_changed(nl));
697 assert(m);
698 assert(m->hdr);
699
700 /* Avoid collisions with outstanding requests */
701 do {
702 picked = nl->serial;
703
704 /* Don't use seq == 0, as that is used for broadcasts, so we would get confused by replies to
705 such messages */
706 nl->serial = nl->serial == UINT32_MAX ? 1 : nl->serial + 1;
707
2b176edc 708 } while (serial_used(nl, picked));
84e10015
ZJS
709
710 m->hdr->nlmsg_seq = picked;
711 message_seal(m);
712}
713
714static int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount) {
715 _cleanup_free_ struct iovec *iovs = NULL;
716 ssize_t k;
717
718 assert(nl);
719 assert(m);
720 assert(msgcount > 0);
721
722 iovs = new(struct iovec, msgcount);
723 if (!iovs)
724 return -ENOMEM;
725
726 for (size_t i = 0; i < msgcount; i++) {
727 assert(m[i]->hdr);
728 assert(m[i]->hdr->nlmsg_len > 0);
729
730 iovs[i] = IOVEC_MAKE(m[i]->hdr, m[i]->hdr->nlmsg_len);
731 }
732
733 k = writev(nl->fd, iovs, msgcount);
734 if (k < 0)
735 return -errno;
736
737 return k;
738}
739
740int sd_netlink_sendv(
741 sd_netlink *nl,
742 sd_netlink_message **messages,
743 size_t msgcount,
744 uint32_t **ret_serial) {
745
746 _cleanup_free_ uint32_t *serials = NULL;
747 int r;
748
749 assert_return(nl, -EINVAL);
750 assert_return(!netlink_pid_changed(nl), -ECHILD);
751 assert_return(messages, -EINVAL);
752 assert_return(msgcount > 0, -EINVAL);
753
754 if (ret_serial) {
755 serials = new(uint32_t, msgcount);
756 if (!serials)
757 return -ENOMEM;
758 }
759
760 for (size_t i = 0; i < msgcount; i++) {
761 assert_return(!messages[i]->sealed, -EPERM);
762
763 netlink_seal_message(nl, messages[i]);
764 if (serials)
765 serials[i] = message_get_serial(messages[i]);
766 }
767
768 r = socket_writev_message(nl, messages, msgcount);
769 if (r < 0)
770 return r;
771
772 if (ret_serial)
773 *ret_serial = TAKE_PTR(serials);
774
775 return r;
776}