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