]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-netlink/netlink-util.c
netlink: move resolve_ifname() or friends to netlink-util.[ch]
[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
434a3483 5#include "format-util.h"
6497a8aa 6#include "memory-util.h"
1c4baffc 7#include "netlink-internal.h"
cf0fbc49 8#include "netlink-util.h"
f6e49154 9#include "parse-util.h"
a5053a15 10#include "strv.h"
d8921c6d 11
1c4baffc 12int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
4afd3348 13 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
434a3483
YW
14 _cleanup_strv_free_ char **alternative_names = NULL;
15 char old_name[IF_NAMESIZE + 1] = {};
d8921c6d
TG
16 int r;
17
18 assert(rtnl);
19 assert(ifindex > 0);
3e137a1b 20 assert(name);
d8921c6d 21
4d643099
YW
22 if (!ifname_valid(name))
23 return -EINVAL;
24
434a3483
YW
25 r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names);
26 if (r < 0)
27 log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m",
28 ifindex);
29
30 if (strv_contains(alternative_names, name)) {
31 r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
4c83d994 32 if (r < 0)
434a3483
YW
33 return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m",
34 name, ifindex);
35
36 format_ifname(ifindex, old_name);
4c83d994
TG
37 }
38
39 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
d8921c6d
TG
40 if (r < 0)
41 return r;
42
1c4baffc 43 r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
3e137a1b
TG
44 if (r < 0)
45 return r;
d8921c6d 46
1c4baffc 47 r = sd_netlink_call(*rtnl, message, 0, NULL);
3e137a1b
TG
48 if (r < 0)
49 return r;
50
434a3483
YW
51 if (!isempty(old_name)) {
52 r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(old_name));
53 if (r < 0)
54 log_debug_errno(r, "Failed to set '%s' as an alternative name on network interface %i, ignoring: %m",
55 old_name, ifindex);
56 }
57
3e137a1b
TG
58 return 0;
59}
60
face9fcc
YW
61int rtnl_set_link_properties(
62 sd_netlink **rtnl,
63 int ifindex,
64 const char *alias,
65 const struct ether_addr *mac,
66 uint32_t txqueues,
67 uint32_t rxqueues,
68 uint32_t txqueuelen,
69 uint32_t mtu,
70 uint32_t gso_max_size,
71 size_t gso_max_segments) {
4afd3348 72 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
3e137a1b
TG
73 int r;
74
75 assert(rtnl);
76 assert(ifindex > 0);
77
face9fcc
YW
78 if (!alias && !mac && txqueues == 0 && rxqueues == 0 && txqueuelen == UINT32_MAX && mtu == 0 &&
79 gso_max_size == 0 && gso_max_segments == 0)
3e137a1b
TG
80 return 0;
81
aedca892 82 if (!*rtnl) {
1c4baffc 83 r = sd_netlink_open(rtnl);
aedca892
TG
84 if (r < 0)
85 return r;
86 }
87
88 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
3e137a1b
TG
89 if (r < 0)
90 return r;
d8921c6d 91
d2df0d0e 92 if (alias) {
1c4baffc 93 r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias);
d2df0d0e
TG
94 if (r < 0)
95 return r;
d2df0d0e
TG
96 }
97
d8921c6d 98 if (mac) {
1c4baffc 99 r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac);
d8921c6d
TG
100 if (r < 0)
101 return r;
d8921c6d
TG
102 }
103
face9fcc
YW
104 if (txqueues > 0) {
105 r = sd_netlink_message_append_u32(message, IFLA_NUM_TX_QUEUES, txqueues);
106 if (r < 0)
107 return r;
108 }
109
110 if (rxqueues > 0) {
111 r = sd_netlink_message_append_u32(message, IFLA_NUM_RX_QUEUES, rxqueues);
112 if (r < 0)
113 return r;
114 }
115
ef4a91a7
116 if (txqueuelen < UINT32_MAX) {
117 r = sd_netlink_message_append_u32(message, IFLA_TXQLEN, txqueuelen);
118 if (r < 0)
119 return r;
120 }
121
4e964aa0 122 if (mtu != 0) {
1c4baffc 123 r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu);
d8921c6d
TG
124 if (r < 0)
125 return r;
d8921c6d
TG
126 }
127
c97cad60
SS
128 if (gso_max_size > 0) {
129 r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SIZE, gso_max_size);
130 if (r < 0)
131 return r;
132 }
133
134 if (gso_max_segments > 0) {
135 r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SEGS, gso_max_segments);
136 if (r < 0)
137 return r;
138 }
139
1c4baffc 140 r = sd_netlink_call(*rtnl, message, 0, NULL);
aedca892
TG
141 if (r < 0)
142 return r;
d8921c6d
TG
143
144 return 0;
145}
3815f36f 146
14982526
YW
147int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
148 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
149 _cleanup_strv_free_ char **names = NULL;
150 int r;
151
152 assert(rtnl);
153 assert(ifindex > 0);
154 assert(ret);
155
156 if (!*rtnl) {
157 r = sd_netlink_open(rtnl);
158 if (r < 0)
159 return r;
160 }
161
162 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
163 if (r < 0)
164 return r;
165
166 r = sd_netlink_call(*rtnl, message, 0, &reply);
167 if (r < 0)
168 return r;
169
170 r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names);
171 if (r < 0 && r != -ENODATA)
172 return r;
173
174 *ret = TAKE_PTR(names);
175
176 return 0;
177}
178
179static int rtnl_update_link_alternative_names(sd_netlink **rtnl, uint16_t nlmsg_type, int ifindex, char * const *alternative_names) {
a5053a15
YW
180 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
181 int r;
182
183 assert(rtnl);
184 assert(ifindex > 0);
14982526 185 assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP));
a5053a15
YW
186
187 if (strv_isempty(alternative_names))
188 return 0;
189
190 if (!*rtnl) {
191 r = sd_netlink_open(rtnl);
192 if (r < 0)
193 return r;
194 }
195
14982526 196 r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex);
a5053a15
YW
197 if (r < 0)
198 return r;
199
200 r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
201 if (r < 0)
202 return r;
203
204 r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names);
205 if (r < 0)
206 return r;
207
208 r = sd_netlink_message_close_container(message);
209 if (r < 0)
210 return r;
211
212 r = sd_netlink_call(*rtnl, message, 0, NULL);
213 if (r < 0)
214 return r;
215
216 return 0;
217}
218
14982526
YW
219int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
220 return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names);
221}
222
223int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
224 return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names);
225}
226
6b50cb5c
YW
227int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names) {
228 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
229 int r;
230
231 assert(rtnl);
232 assert(ifname);
233
234 if (strv_isempty(alternative_names))
235 return 0;
236
237 if (!*rtnl) {
238 r = sd_netlink_open(rtnl);
239 if (r < 0)
240 return r;
241 }
242
243 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, 0);
244 if (r < 0)
245 return r;
246
247 r = sd_netlink_message_append_string(message, IFLA_IFNAME, ifname);
248 if (r < 0)
249 return r;
250
251 r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
252 if (r < 0)
253 return r;
254
255 r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, alternative_names);
256 if (r < 0)
257 return r;
258
259 r = sd_netlink_message_close_container(message);
260 if (r < 0)
261 return r;
262
263 r = sd_netlink_call(*rtnl, message, 0, NULL);
264 if (r < 0)
265 return r;
266
267 return 0;
268}
269
231d9de1 270int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name) {
bad7cecc 271 _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
b04c5e51 272 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
231d9de1 273 int r, ret;
b04c5e51 274
b04c5e51 275 assert(name);
b04c5e51 276
bad7cecc
ZJS
277 if (!rtnl)
278 rtnl = &our_rtnl;
b04c5e51
YW
279 if (!*rtnl) {
280 r = sd_netlink_open(rtnl);
281 if (r < 0)
282 return r;
283 }
284
285 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
286 if (r < 0)
287 return r;
288
289 r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
290 if (r < 0)
291 return r;
292
293 r = sd_netlink_call(*rtnl, message, 0, &reply);
d308bb99
ZJS
294 if (r == -EINVAL)
295 return -ENODEV; /* The device doesn't exist */
b04c5e51
YW
296 if (r < 0)
297 return r;
298
231d9de1
ZJS
299 r = sd_rtnl_message_link_get_ifindex(reply, &ret);
300 if (r < 0)
301 return r;
302 assert(ret > 0);
303 return ret;
b04c5e51
YW
304}
305
f6e49154
YW
306int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name) {
307 int r;
308
309 /* Like if_nametoindex, but resolves "alternative names" too. */
310
311 assert(name);
312
313 r = if_nametoindex(name);
314 if (r > 0)
315 return r;
316
317 return rtnl_resolve_link_alternative_name(rtnl, name);
318}
319
320int rtnl_resolve_interface(sd_netlink **rtnl, const char *name) {
321 int r;
322
323 /* Like rtnl_resolve_ifname, but resolves interface numbers too. */
324
325 assert(name);
326
327 r = parse_ifindex(name);
328 if (r > 0)
329 return r;
330 assert(r < 0);
331
332 return rtnl_resolve_ifname(rtnl, name);
333}
334
335int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
336 int r;
337
338 r = rtnl_resolve_interface(rtnl, name);
339 if (r < 0)
340 return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
341 return r;
342}
343
f25e642b 344int rtnl_get_link_info(sd_netlink **rtnl, int ifindex, unsigned short *ret_iftype, unsigned *ret_flags) {
ef62949a 345 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
f25e642b
YW
346 unsigned short iftype;
347 unsigned flags;
ef62949a
YW
348 int r;
349
f25e642b
YW
350 assert(rtnl);
351 assert(ifindex > 0);
352
353 if (!ret_iftype && !ret_flags)
354 return 0;
355
ef62949a
YW
356 if (!*rtnl) {
357 r = sd_netlink_open(rtnl);
358 if (r < 0)
359 return r;
360 }
361
362 r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
363 if (r < 0)
364 return r;
365
366 r = sd_netlink_call(*rtnl, message, 0, &reply);
367 if (r == -EINVAL)
368 return -ENODEV; /* The device does not exist */
369 if (r < 0)
370 return r;
371
f25e642b
YW
372 if (ret_iftype) {
373 r = sd_rtnl_message_link_get_type(reply, &iftype);
374 if (r < 0)
375 return r;
376 }
377
378 if (ret_flags) {
379 r = sd_rtnl_message_link_get_flags(reply, &flags);
380 if (r < 0)
381 return r;
382 }
383
384 if (ret_iftype)
385 *ret_iftype = iftype;
386 if (ret_flags)
387 *ret_flags = flags;
388 return 0;
ef62949a
YW
389}
390
05d0c2e3 391int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
3815f36f
TG
392 struct nlmsgerr *err;
393 int r;
394
395 assert(error <= 0);
396
05d0c2e3 397 r = message_new(rtnl, ret, NLMSG_ERROR);
3815f36f
TG
398 if (r < 0)
399 return r;
400
628f08b6 401 rtnl_message_seal(*ret);
3815f36f
TG
402 (*ret)->hdr->nlmsg_seq = serial;
403
404 err = NLMSG_DATA((*ret)->hdr);
3815f36f
TG
405 err->error = error;
406
407 return 0;
408}
409
ee8c4568 410int rtnl_log_parse_error(int r) {
8d3d7072 411 return log_error_errno(r, "Failed to parse netlink message: %m");
ee8c4568
LP
412}
413
414int rtnl_log_create_error(int r) {
8d3d7072 415 return log_error_errno(r, "Failed to create netlink message: %m");
ee8c4568 416}
6497a8aa
YW
417
418void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) {
419 size_t padding_length;
14b6e6b6 420 uint8_t *padding;
6497a8aa
YW
421
422 assert(rta);
423 assert(!data || data_length > 0);
424
425 /* fill in the attribute */
426 rta->rta_type = type;
427 rta->rta_len = RTA_LENGTH(data_length);
428 if (data)
429 /* we don't deal with the case where the user lies about the type
430 * and gives us too little data (so don't do that)
431 */
432 padding = mempcpy(RTA_DATA(rta), data, data_length);
433
434 else
435 /* if no data was passed, make sure we still initialize the padding
436 note that we can have data_length > 0 (used by some containers) */
437 padding = RTA_DATA(rta);
438
439 /* make sure also the padding at the end of the message is initialized */
14b6e6b6 440 padding_length = (uint8_t *) rta + RTA_SPACE(data_length) - padding;
6497a8aa
YW
441 memzero(padding, padding_length);
442}
443
444int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) {
445 struct rtattr *new_rta, *sub_rta;
446 size_t message_length;
447
448 assert(rta);
449 assert(!data || data_length > 0);
450
451 /* get the new message size (with padding at the end) */
452 message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length);
453
454 /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
455 if (message_length > MIN(page_size(), 8192UL))
456 return -ENOBUFS;
457
458 /* realloc to fit the new attribute */
459 new_rta = realloc(*rta, message_length);
460 if (!new_rta)
461 return -ENOMEM;
462 *rta = new_rta;
463
464 /* get pointer to the attribute we are about to add */
465 sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len));
466
467 rtattr_append_attribute_internal(sub_rta, type, data, data_length);
468
469 /* update rta_len */
470 (*rta)->rta_len = message_length;
471
472 return 0;
473}
2fe1d557 474
e8f52f3c
YW
475MultipathRoute *multipath_route_free(MultipathRoute *m) {
476 if (!m)
477 return NULL;
478
479 free(m->ifname);
480
481 return mfree(m);
482}
483
4867b9d7
YW
484int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret) {
485 _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
486 _cleanup_free_ char *ifname = NULL;
487
488 assert(m);
489 assert(ret);
490
491 if (m->ifname) {
492 ifname = strdup(m->ifname);
493 if (!ifname)
494 return -ENOMEM;
495 }
496
497 n = new(MultipathRoute, 1);
498 if (!n)
499 return -ENOMEM;
500
501 *n = (MultipathRoute) {
502 .gateway = m->gateway,
503 .weight = m->weight,
504 .ifindex = m->ifindex,
505 .ifname = TAKE_PTR(ifname),
506 };
507
508 *ret = TAKE_PTR(n);
509
510 return 0;
511}
512
2fe1d557
YW
513int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) {
514 _cleanup_ordered_set_free_free_ OrderedSet *set = NULL;
515 int r;
516
517 assert(rtnh);
518 assert(IN_SET(family, AF_INET, AF_INET6));
519
520 if (size < sizeof(struct rtnexthop))
521 return -EBADMSG;
522
523 for (; size >= sizeof(struct rtnexthop); ) {
e8f52f3c 524 _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
2fe1d557
YW
525
526 if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
527 return -EBADMSG;
528
529 if (rtnh->rtnh_len < sizeof(struct rtnexthop))
530 return -EBADMSG;
531
532 m = new(MultipathRoute, 1);
533 if (!m)
534 return -ENOMEM;
535
536 *m = (MultipathRoute) {
537 .ifindex = rtnh->rtnh_ifindex,
234106db 538 .weight = rtnh->rtnh_hops,
2fe1d557
YW
539 };
540
541 if (rtnh->rtnh_len > sizeof(struct rtnexthop)) {
542 size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop);
543
544 for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
545 if (attr->rta_type == RTA_GATEWAY) {
546 if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family)))
547 return -EBADMSG;
548
549 m->gateway.family = family;
550 memcpy(&m->gateway.address, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
551 break;
552 } else if (attr->rta_type == RTA_VIA) {
553 uint16_t gw_family;
554
555 if (family != AF_INET)
556 return -EINVAL;
557
558 if (attr->rta_len < RTA_LENGTH(sizeof(uint16_t)))
559 return -EBADMSG;
560
561 gw_family = *(uint16_t *) RTA_DATA(attr);
562
563 if (gw_family != AF_INET6)
564 return -EBADMSG;
565
566 if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family)))
567 return -EBADMSG;
568
569 memcpy(&m->gateway, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family));
570 break;
571 }
572 }
573 }
574
575 r = ordered_set_ensure_put(&set, NULL, m);
576 if (r < 0)
577 return r;
578
579 TAKE_PTR(m);
580
581 size -= NLMSG_ALIGN(rtnh->rtnh_len);
582 rtnh = RTNH_NEXT(rtnh);
583 }
584
585 if (ret)
586 *ret = TAKE_PTR(set);
587 return 0;
588}