1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/nexthop.h>
5 #include "alloc-util.h"
6 #include "extract-word.h"
7 #include "netlink-util.h"
8 #include "networkd-manager.h"
9 #include "networkd-network.h"
10 #include "networkd-nexthop.h"
11 #include "networkd-route.h"
12 #include "networkd-route-nexthop.h"
13 #include "networkd-route-util.h"
14 #include "parse-util.h"
15 #include "string-util.h"
17 void route_detach_from_nexthop(Route
*route
) {
21 assert(route
->manager
);
23 if (route
->nexthop_id
== 0)
26 if (nexthop_get_by_id(route
->manager
, route
->nexthop_id
, &nh
) < 0)
29 route_unref(set_remove(nh
->routes
, route
));
32 void route_attach_to_nexthop(Route
*route
) {
37 assert(route
->manager
);
39 if (route
->nexthop_id
== 0)
42 r
= nexthop_get_by_id(route
->manager
, route
->nexthop_id
, &nh
);
44 if (route
->manager
->manage_foreign_nexthops
)
45 log_debug_errno(r
, "Route has unknown nexthop ID (%"PRIu32
"), ignoring.",
50 r
= set_ensure_put(&nh
->routes
, &route_hash_ops_unref
, route
);
52 return (void) log_debug_errno(r
, "Failed to save route to nexthop, ignoring: %m");
54 return (void) log_debug("Duplicated route assigned to nexthop, ignoring.");
59 static void route_nexthop_done(RouteNextHop
*nh
) {
65 RouteNextHop
* route_nexthop_free(RouteNextHop
*nh
) {
69 route_nexthop_done(nh
);
74 void route_nexthops_done(Route
*route
) {
77 route_nexthop_done(&route
->nexthop
);
78 ordered_set_free(route
->nexthops
);
81 static void route_nexthop_hash_func_full(const RouteNextHop
*nh
, struct siphash
*state
, bool with_weight
) {
85 /* See nh_comp() in net/ipv4/fib_semantics.c of the kernel. */
87 siphash24_compress_typesafe(nh
->family
, state
);
88 if (!IN_SET(nh
->family
, AF_INET
, AF_INET6
))
91 in_addr_hash_func(&nh
->gw
, nh
->family
, state
);
93 siphash24_compress_typesafe(nh
->weight
, state
);
94 siphash24_compress_typesafe(nh
->ifindex
, state
);
96 siphash24_compress_string(nh
->ifname
, state
); /* For Network or Request object. */
99 static int route_nexthop_compare_func_full(const RouteNextHop
*a
, const RouteNextHop
*b
, bool with_weight
) {
105 r
= CMP(a
->family
, b
->family
);
109 if (!IN_SET(a
->family
, AF_INET
, AF_INET6
))
112 r
= memcmp(&a
->gw
, &b
->gw
, FAMILY_ADDRESS_SIZE(a
->family
));
117 r
= CMP(a
->weight
, b
->weight
);
122 r
= CMP(a
->ifindex
, b
->ifindex
);
126 if (a
->ifindex
== 0) {
127 r
= strcmp_ptr(a
->ifname
, b
->ifname
);
135 static void route_nexthop_hash_func(const RouteNextHop
*nh
, struct siphash
*state
) {
136 route_nexthop_hash_func_full(nh
, state
, /* with_weight = */ true);
139 static int route_nexthop_compare_func(const RouteNextHop
*a
, const RouteNextHop
*b
) {
140 return route_nexthop_compare_func_full(a
, b
, /* with_weight = */ true);
143 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
144 route_nexthop_hash_ops
,
146 route_nexthop_hash_func
,
147 route_nexthop_compare_func
,
150 static size_t route_n_nexthops(const Route
*route
) {
151 if (route
->nexthop_id
!= 0 || route_type_is_reject(route
))
154 if (ordered_set_isempty(route
->nexthops
))
157 return ordered_set_size(route
->nexthops
);
160 void route_nexthops_hash_func(const Route
*route
, struct siphash
*state
) {
163 size_t nhs
= route_n_nexthops(route
);
164 siphash24_compress_typesafe(nhs
, state
);
168 siphash24_compress_typesafe(route
->nexthop_id
, state
);
172 route_nexthop_hash_func_full(&route
->nexthop
, state
, /* with_weight = */ false);
177 ORDERED_SET_FOREACH(nh
, route
->nexthops
)
178 route_nexthop_hash_func(nh
, state
);
182 int route_nexthops_compare_func(const Route
*a
, const Route
*b
) {
188 size_t a_nhs
= route_n_nexthops(a
);
189 size_t b_nhs
= route_n_nexthops(b
);
190 r
= CMP(a_nhs
, b_nhs
);
196 return CMP(a
->nexthop_id
, b
->nexthop_id
);
199 return route_nexthop_compare_func_full(&a
->nexthop
, &b
->nexthop
, /* with_weight = */ false);
203 ORDERED_SET_FOREACH(nh
, a
->nexthops
) {
204 r
= CMP(nh
, (RouteNextHop
*) ordered_set_get(a
->nexthops
, nh
));
212 static int route_nexthop_copy(const RouteNextHop
*src
, RouteNextHop
*dest
) {
218 /* unset pointer copied in the above. */
221 return strdup_or_null(src
->ifindex
> 0 ? NULL
: src
->ifname
, &dest
->ifname
);
224 static int route_nexthop_dup(const RouteNextHop
*src
, RouteNextHop
**ret
) {
225 _cleanup_(route_nexthop_freep
) RouteNextHop
*dest
= NULL
;
231 dest
= new(RouteNextHop
, 1);
235 r
= route_nexthop_copy(src
, dest
);
239 *ret
= TAKE_PTR(dest
);
243 int route_nexthops_copy(const Route
*src
, const RouteNextHop
*nh
, Route
*dest
) {
249 if (src
->nexthop_id
!= 0 || route_type_is_reject(src
))
253 return route_nexthop_copy(nh
, &dest
->nexthop
);
255 if (ordered_set_isempty(src
->nexthops
))
256 return route_nexthop_copy(&src
->nexthop
, &dest
->nexthop
);
258 ORDERED_SET_FOREACH(nh
, src
->nexthops
) {
259 _cleanup_(route_nexthop_freep
) RouteNextHop
*nh_dup
= NULL
;
261 r
= route_nexthop_dup(nh
, &nh_dup
);
265 r
= ordered_set_ensure_put(&dest
->nexthops
, &route_nexthop_hash_ops
, nh_dup
);
276 static bool multipath_routes_needs_adjust(const Route
*route
) {
280 ORDERED_SET_FOREACH(nh
, route
->nexthops
)
281 if (route
->nexthop
.ifindex
== 0)
287 bool route_nexthops_needs_adjust(const Route
*route
) {
290 if (route
->nexthop_id
!= 0)
291 /* At this stage, the nexthop may not be configured, or may be under reconfiguring.
292 * Hence, we cannot know if the nexthop is blackhole or not. */
293 return route
->type
!= RTN_BLACKHOLE
;
295 if (route_type_is_reject(route
))
298 if (ordered_set_isempty(route
->nexthops
))
299 return route
->nexthop
.ifindex
== 0;
301 return multipath_routes_needs_adjust(route
);
304 static bool route_nexthop_set_ifindex(RouteNextHop
*nh
, Link
*link
) {
307 assert(link
->manager
);
309 if (nh
->ifindex
> 0) {
310 nh
->ifname
= mfree(nh
->ifname
);
314 /* If an interface name is specified, use it. Otherwise, use the interface that requests this route. */
315 if (nh
->ifname
&& link_get_by_name(link
->manager
, nh
->ifname
, &link
) < 0)
318 nh
->ifindex
= link
->ifindex
;
319 nh
->ifname
= mfree(nh
->ifname
);
320 return true; /* updated */
323 int route_adjust_nexthops(Route
*route
, Link
*link
) {
328 assert(link
->manager
);
330 /* When an IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel sends
331 * RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type fib_rt_info::type
332 * may not be blackhole. Thus, we cannot know the internal value. Moreover, on route removal, the
333 * matching is done with the hidden value if we set non-zero type in RTM_DELROUTE message. So,
334 * here let's set route type to BLACKHOLE when the nexthop is blackhole. */
335 if (route
->nexthop_id
!= 0) {
338 r
= nexthop_is_ready(link
->manager
, route
->nexthop_id
, &nexthop
);
340 return r
; /* r == 0 means the nexthop is under (re-)configuring.
341 * We cannot use the currently remembered information. */
343 if (!nexthop
->blackhole
)
346 if (route
->type
== RTN_BLACKHOLE
)
349 route
->type
= RTN_BLACKHOLE
;
350 return true; /* updated */
353 if (route_type_is_reject(route
))
356 if (ordered_set_isempty(route
->nexthops
))
357 return route_nexthop_set_ifindex(&route
->nexthop
, link
);
359 if (!multipath_routes_needs_adjust(route
))
362 _cleanup_ordered_set_free_ OrderedSet
*nexthops
= NULL
;
364 _cleanup_(route_nexthop_freep
) RouteNextHop
*nh
= NULL
;
366 nh
= ordered_set_steal_first(route
->nexthops
);
370 (void) route_nexthop_set_ifindex(nh
, link
);
372 r
= ordered_set_ensure_put(&nexthops
, &route_nexthop_hash_ops
, nh
);
374 continue; /* Duplicated? Let's drop the nexthop. */
382 ordered_set_free(route
->nexthops
);
383 route
->nexthops
= TAKE_PTR(nexthops
);
384 return true; /* updated */
387 int route_nexthop_get_link(Manager
*manager
, const RouteNextHop
*nh
, Link
**ret
) {
392 return link_get_by_index(manager
, nh
->ifindex
, ret
);
394 return link_get_by_name(manager
, nh
->ifname
, ret
);
399 static bool route_nexthop_is_ready_to_configure(const RouteNextHop
*nh
, Manager
*manager
, bool onlink
) {
405 if (route_nexthop_get_link(manager
, nh
, &link
) < 0)
408 if (!link_is_ready_to_configure(link
, /* allow_unmanaged = */ true))
411 /* If the interface is not managed by us, we request that the interface has carrier.
412 * That is, ConfigureWithoutCarrier=no is the default even for unamanaged interfaces. */
413 if (!link
->network
&& !link_has_carrier(link
))
416 return gateway_is_ready(link
, onlink
, nh
->family
, &nh
->gw
);
419 int route_nexthops_is_ready_to_configure(const Route
*route
, Manager
*manager
) {
425 if (route
->nexthop_id
!= 0) {
426 struct nexthop_grp
*nhg
;
429 r
= nexthop_is_ready(manager
, route
->nexthop_id
, &nh
);
433 HASHMAP_FOREACH(nhg
, nh
->group
) {
434 r
= nexthop_is_ready(manager
, nhg
->id
, NULL
);
442 if (route_type_is_reject(route
))
445 if (ordered_set_isempty(route
->nexthops
))
446 return route_nexthop_is_ready_to_configure(&route
->nexthop
, manager
, FLAGS_SET(route
->flags
, RTNH_F_ONLINK
));
449 ORDERED_SET_FOREACH(nh
, route
->nexthops
)
450 if (!route_nexthop_is_ready_to_configure(nh
, manager
, FLAGS_SET(route
->flags
, RTNH_F_ONLINK
)))
456 int route_nexthops_to_string(const Route
*route
, char **ret
) {
457 _cleanup_free_
char *buf
= NULL
;
463 if (route
->nexthop_id
!= 0) {
464 if (asprintf(&buf
, "nexthop: %"PRIu32
, route
->nexthop_id
) < 0)
467 *ret
= TAKE_PTR(buf
);
471 if (route_type_is_reject(route
)) {
472 buf
= strdup("gw: n/a");
476 *ret
= TAKE_PTR(buf
);
480 if (ordered_set_isempty(route
->nexthops
)) {
481 if (in_addr_is_set(route
->nexthop
.family
, &route
->nexthop
.gw
))
482 buf
= strjoin("gw: ", IN_ADDR_TO_STRING(route
->nexthop
.family
, &route
->nexthop
.gw
));
483 else if (route
->gateway_from_dhcp_or_ra
) {
484 if (route
->nexthop
.family
== AF_INET
)
485 buf
= strdup("gw: _dhcp4");
486 else if (route
->nexthop
.family
== AF_INET6
)
487 buf
= strdup("gw: _ipv6ra");
489 buf
= strdup("gw: _dhcp");
491 buf
= strdup("gw: n/a");
495 *ret
= TAKE_PTR(buf
);
500 ORDERED_SET_FOREACH(nh
, route
->nexthops
) {
501 const char *s
= in_addr_is_set(nh
->family
, &nh
->gw
) ? IN_ADDR_TO_STRING(nh
->family
, &nh
->gw
) : NULL
;
504 r
= strextendf_with_separator(&buf
, ",", "%s@%i:%"PRIu32
, strempty(s
), nh
->ifindex
, nh
->weight
+ 1);
506 r
= strextendf_with_separator(&buf
, ",", "%s@%s:%"PRIu32
, strempty(s
), nh
->ifname
, nh
->weight
+ 1);
508 r
= strextendf_with_separator(&buf
, ",", "%s:%"PRIu32
, strempty(s
), nh
->weight
+ 1);
513 char *p
= strjoin("gw: ", strna(buf
));
521 static int append_nexthop_one(const Route
*route
, const RouteNextHop
*nh
, struct rtattr
**rta
, size_t offset
) {
522 struct rtnexthop
*rtnh
;
523 struct rtattr
*new_rta
;
527 assert(IN_SET(route
->family
, AF_INET
, AF_INET6
));
532 new_rta
= realloc(*rta
, RTA_ALIGN((*rta
)->rta_len
) + RTA_SPACE(sizeof(struct rtnexthop
)));
537 rtnh
= (struct rtnexthop
*)((uint8_t *) *rta
+ offset
);
538 *rtnh
= (struct rtnexthop
) {
539 .rtnh_len
= sizeof(*rtnh
),
540 .rtnh_ifindex
= nh
->ifindex
,
541 .rtnh_hops
= nh
->weight
,
544 (*rta
)->rta_len
+= sizeof(struct rtnexthop
);
546 if (nh
->family
== route
->family
) {
547 r
= rtattr_append_attribute(rta
, RTA_GATEWAY
, &nh
->gw
, FAMILY_ADDRESS_SIZE(nh
->family
));
551 rtnh
= (struct rtnexthop
*)((uint8_t *) *rta
+ offset
);
552 rtnh
->rtnh_len
+= RTA_SPACE(FAMILY_ADDRESS_SIZE(nh
->family
));
554 } else if (nh
->family
== AF_INET6
) {
555 assert(route
->family
== AF_INET
);
557 r
= rtattr_append_attribute(rta
, RTA_VIA
,
559 .family
= nh
->family
,
561 }, sizeof(RouteVia
));
565 rtnh
= (struct rtnexthop
*)((uint8_t *) *rta
+ offset
);
566 rtnh
->rtnh_len
+= RTA_SPACE(sizeof(RouteVia
));
568 } else if (nh
->family
== AF_INET
)
569 assert_not_reached();
574 (*rta
)->rta_len
-= sizeof(struct rtnexthop
);
578 static int netlink_message_append_multipath_route(const Route
*route
, sd_netlink_message
*message
) {
579 _cleanup_free_
struct rtattr
*rta
= NULL
;
586 rta
= new(struct rtattr
, 1);
590 *rta
= (struct rtattr
) {
591 .rta_type
= RTA_MULTIPATH
,
592 .rta_len
= RTA_LENGTH(0),
594 offset
= (uint8_t *) RTA_DATA(rta
) - (uint8_t *) rta
;
596 if (ordered_set_isempty(route
->nexthops
)) {
597 r
= append_nexthop_one(route
, &route
->nexthop
, &rta
, offset
);
603 ORDERED_SET_FOREACH(nh
, route
->nexthops
) {
604 struct rtnexthop
*rtnh
;
606 r
= append_nexthop_one(route
, nh
, &rta
, offset
);
610 rtnh
= (struct rtnexthop
*)((uint8_t *) rta
+ offset
);
611 offset
= (uint8_t *) RTNH_NEXT(rtnh
) - (uint8_t *) rta
;
615 return sd_netlink_message_append_data(message
, RTA_MULTIPATH
, RTA_DATA(rta
), RTA_PAYLOAD(rta
));
618 int route_nexthops_set_netlink_message(const Route
*route
, sd_netlink_message
*message
) {
622 assert(IN_SET(route
->family
, AF_INET
, AF_INET6
));
625 if (route
->nexthop_id
!= 0)
626 return sd_netlink_message_append_u32(message
, RTA_NH_ID
, route
->nexthop_id
);
628 if (route_type_is_reject(route
))
631 /* We request IPv6 multipath routes separately. Even though, if weight is non-zero, we need to use
632 * RTA_MULTIPATH, as we have no way to specify the weight of the nexthop. */
633 if (ordered_set_isempty(route
->nexthops
) && route
->nexthop
.weight
== 0) {
635 if (in_addr_is_set(route
->nexthop
.family
, &route
->nexthop
.gw
)) {
636 if (route
->nexthop
.family
== route
->family
)
637 r
= netlink_message_append_in_addr_union(message
, RTA_GATEWAY
, route
->nexthop
.family
, &route
->nexthop
.gw
);
639 assert(route
->family
== AF_INET
);
640 r
= sd_netlink_message_append_data(message
, RTA_VIA
,
642 .family
= route
->nexthop
.family
,
643 .address
= route
->nexthop
.gw
,
644 }, sizeof(RouteVia
));
650 assert(route
->nexthop
.ifindex
> 0);
651 return sd_netlink_message_append_u32(message
, RTA_OIF
, route
->nexthop
.ifindex
);
654 return netlink_message_append_multipath_route(route
, message
);
657 static int route_parse_nexthops(Route
*route
, const struct rtnexthop
*rtnh
, size_t size
) {
658 _cleanup_ordered_set_free_ OrderedSet
*nexthops
= NULL
;
662 assert(IN_SET(route
->family
, AF_INET
, AF_INET6
));
665 if (size
< sizeof(struct rtnexthop
))
668 for (; size
>= sizeof(struct rtnexthop
); ) {
669 _cleanup_(route_nexthop_freep
) RouteNextHop
*nh
= NULL
;
671 if (NLMSG_ALIGN(rtnh
->rtnh_len
) > size
)
674 if (rtnh
->rtnh_len
< sizeof(struct rtnexthop
))
677 nh
= new(RouteNextHop
, 1);
681 *nh
= (RouteNextHop
) {
682 .ifindex
= rtnh
->rtnh_ifindex
,
683 .weight
= rtnh
->rtnh_hops
,
686 if (rtnh
->rtnh_len
> sizeof(struct rtnexthop
)) {
687 size_t len
= rtnh
->rtnh_len
- sizeof(struct rtnexthop
);
688 bool have_gw
= false;
690 for (struct rtattr
*attr
= RTNH_DATA(rtnh
); RTA_OK(attr
, len
); attr
= RTA_NEXT(attr
, len
)) {
692 switch (attr
->rta_type
) {
697 if (attr
->rta_len
!= RTA_LENGTH(FAMILY_ADDRESS_SIZE(route
->family
)))
700 nh
->family
= route
->family
;
701 memcpy(&nh
->gw
, RTA_DATA(attr
), FAMILY_ADDRESS_SIZE(nh
->family
));
709 if (route
->family
!= AF_INET
)
712 if (attr
->rta_len
!= RTA_LENGTH(sizeof(RouteVia
)))
715 RouteVia
*via
= RTA_DATA(attr
);
716 if (via
->family
!= AF_INET6
)
719 nh
->family
= via
->family
;
720 nh
->gw
= via
->address
;
727 r
= ordered_set_ensure_put(&nexthops
, &route_nexthop_hash_ops
, nh
);
731 else if (r
!= -EEXIST
)
734 size
-= NLMSG_ALIGN(rtnh
->rtnh_len
);
735 rtnh
= RTNH_NEXT(rtnh
);
738 ordered_set_free(route
->nexthops
);
739 route
->nexthops
= TAKE_PTR(nexthops
);
743 int route_nexthops_read_netlink_message(Route
*route
, sd_netlink_message
*message
) {
749 r
= sd_netlink_message_read_u32(message
, RTA_NH_ID
, &route
->nexthop_id
);
750 if (r
< 0 && r
!= -ENODATA
)
751 return log_warning_errno(r
, "rtnl: received route message with invalid nexthop id, ignoring: %m");
753 if (route
->nexthop_id
!= 0 || route_type_is_reject(route
))
754 /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
755 * fib6_nh_init() in net/ipv6/route.c. However, we'd like to make it consistent with IPv4
756 * routes. Hence, skip reading of RTA_OIF. */
760 r
= sd_netlink_message_read_u32(message
, RTA_OIF
, &ifindex
);
762 route
->nexthop
.ifindex
= (int) ifindex
;
763 else if (r
!= -ENODATA
)
764 return log_warning_errno(r
, "rtnl: could not get ifindex from route message, ignoring: %m");
766 if (route
->nexthop
.ifindex
> 0) {
767 r
= netlink_message_read_in_addr_union(message
, RTA_GATEWAY
, route
->family
, &route
->nexthop
.gw
);
769 route
->nexthop
.family
= route
->family
;
773 return log_warning_errno(r
, "rtnl: received route message without valid gateway, ignoring: %m");
775 if (route
->family
!= AF_INET
)
779 r
= sd_netlink_message_read(message
, RTA_VIA
, sizeof(via
), &via
);
781 route
->nexthop
.family
= via
.family
;
782 route
->nexthop
.gw
= via
.address
;
786 return log_warning_errno(r
, "rtnl: received route message without valid gateway, ignoring: %m");
792 _cleanup_free_
void *rta
= NULL
;
793 r
= sd_netlink_message_read_data(message
, RTA_MULTIPATH
, &rta_len
, &rta
);
797 return log_warning_errno(r
, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
799 r
= route_parse_nexthops(route
, rta
, rta_len
);
801 return log_warning_errno(r
, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
806 int route_section_verify_nexthops(Route
*route
) {
808 assert(route
->section
);
810 if (route
->gateway_from_dhcp_or_ra
) {
811 assert(route
->network
);
813 if (route
->nexthop
.family
== AF_UNSPEC
)
814 /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
815 switch (route
->family
) {
817 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
818 "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
819 route
->section
->filename
, route
->section
->line
);
821 route
->nexthop
.family
= route
->family
= AF_INET
;
825 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
826 "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
827 route
->section
->filename
, route
->section
->line
, route
->family
== AF_INET
? "_dhcp4" : "_ipv6ra");
829 route
->nexthop
.family
= route
->family
;
832 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
833 "%s: Invalid route family. Ignoring [Route] section from line %u.",
834 route
->section
->filename
, route
->section
->line
);
837 if (route
->nexthop
.family
== AF_INET
&& !FLAGS_SET(route
->network
->dhcp
, ADDRESS_FAMILY_IPV4
))
838 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
839 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
840 "Ignoring [Route] section from line %u.",
841 route
->section
->filename
, route
->section
->line
);
843 if (route
->nexthop
.family
== AF_INET6
&& !route
->network
->ipv6_accept_ra
)
844 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
845 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
846 "Ignoring [Route] section from line %u.",
847 route
->section
->filename
, route
->section
->line
);
850 /* When only Gateway= is specified, assume the route family based on the Gateway address. */
851 if (route
->family
== AF_UNSPEC
)
852 route
->family
= route
->nexthop
.family
;
854 if (route
->family
== AF_UNSPEC
) {
855 assert(route
->section
);
857 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
858 "%s: Route section without Gateway=, Destination=, Source=, "
859 "or PreferredSource= field configured. "
860 "Ignoring [Route] section from line %u.",
861 route
->section
->filename
, route
->section
->line
);
864 if (route
->gateway_onlink
< 0 && in_addr_is_set(route
->nexthop
.family
, &route
->nexthop
.gw
) &&
865 route
->network
&& ordered_hashmap_isempty(route
->network
->addresses_by_section
)) {
866 /* If no address is configured, in most cases the gateway cannot be reachable.
867 * TODO: we may need to improve the condition above. */
868 log_warning("%s: Gateway= without static address configured. "
869 "Enabling GatewayOnLink= option.",
870 route
->section
->filename
);
871 route
->gateway_onlink
= true;
874 if (route
->gateway_onlink
>= 0)
875 SET_FLAG(route
->flags
, RTNH_F_ONLINK
, route
->gateway_onlink
);
877 if (route
->family
== AF_INET6
) {
878 if (route
->nexthop
.family
== AF_INET
)
879 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
880 "%s: IPv4 gateway is configured for IPv6 route. "
881 "Ignoring [Route] section from line %u.",
882 route
->section
->filename
, route
->section
->line
);
885 ORDERED_SET_FOREACH(nh
, route
->nexthops
)
886 if (nh
->family
== AF_INET
)
887 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
888 "%s: IPv4 multipath route is specified for IPv6 route. "
889 "Ignoring [Route] section from line %u.",
890 route
->section
->filename
, route
->section
->line
);
893 if (route
->nexthop_id
!= 0 &&
894 (route
->gateway_from_dhcp_or_ra
||
895 in_addr_is_set(route
->nexthop
.family
, &route
->nexthop
.gw
) ||
896 !ordered_set_isempty(route
->nexthops
)))
897 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
898 "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
899 "Ignoring [Route] section from line %u.",
900 route
->section
->filename
, route
->section
->line
);
902 if (route_type_is_reject(route
) &&
903 (route
->gateway_from_dhcp_or_ra
||
904 in_addr_is_set(route
->nexthop
.family
, &route
->nexthop
.gw
) ||
905 !ordered_set_isempty(route
->nexthops
)))
906 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
907 "%s: reject type route cannot be specified with Gateway= or MultiPathRoute=. "
908 "Ignoring [Route] section from line %u.",
909 route
->section
->filename
, route
->section
->line
);
911 if ((route
->gateway_from_dhcp_or_ra
||
912 in_addr_is_set(route
->nexthop
.family
, &route
->nexthop
.gw
)) &&
913 !ordered_set_isempty(route
->nexthops
))
914 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
915 "%s: Gateway= cannot be specified with MultiPathRoute=. "
916 "Ignoring [Route] section from line %u.",
917 route
->section
->filename
, route
->section
->line
);
919 if (ordered_set_size(route
->nexthops
) == 1) {
920 _cleanup_(route_nexthop_freep
) RouteNextHop
*nh
= ordered_set_steal_first(route
->nexthops
);
922 route_nexthop_done(&route
->nexthop
);
923 route
->nexthop
= TAKE_STRUCT(*nh
);
925 assert(ordered_set_isempty(route
->nexthops
));
926 route
->nexthops
= ordered_set_free(route
->nexthops
);
932 int config_parse_gateway(
934 const char *filename
,
937 unsigned section_line
,
944 Network
*network
= userdata
;
945 _cleanup_(route_unref_or_set_invalidp
) Route
*route
= NULL
;
954 if (streq(section
, "Network")) {
955 /* we are not in an Route section, so use line number instead */
956 r
= route_new_static(network
, filename
, line
, &route
);
960 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
961 "Failed to allocate route, ignoring assignment: %m");
965 r
= route_new_static(network
, filename
, section_line
, &route
);
969 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
970 "Failed to allocate route, ignoring assignment: %m");
974 if (isempty(rvalue
)) {
975 route
->gateway_from_dhcp_or_ra
= false;
976 route
->nexthop
.family
= AF_UNSPEC
;
977 route
->nexthop
.gw
= IN_ADDR_NULL
;
982 if (streq(rvalue
, "_dhcp")) {
983 route
->gateway_from_dhcp_or_ra
= true;
984 route
->nexthop
.family
= AF_UNSPEC
;
985 route
->nexthop
.gw
= IN_ADDR_NULL
;
990 if (streq(rvalue
, "_dhcp4")) {
991 route
->gateway_from_dhcp_or_ra
= true;
992 route
->nexthop
.family
= AF_INET
;
993 route
->nexthop
.gw
= IN_ADDR_NULL
;
998 if (streq(rvalue
, "_ipv6ra")) {
999 route
->gateway_from_dhcp_or_ra
= true;
1000 route
->nexthop
.family
= AF_INET6
;
1001 route
->nexthop
.gw
= IN_ADDR_NULL
;
1007 r
= in_addr_from_string_auto(rvalue
, &route
->nexthop
.family
, &route
->nexthop
.gw
);
1009 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1010 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);
1014 route
->gateway_from_dhcp_or_ra
= false;
1019 int config_parse_route_gateway_onlink(
1021 const char *filename
,
1023 const char *section
,
1024 unsigned section_line
,
1031 Network
*network
= userdata
;
1032 _cleanup_(route_unref_or_set_invalidp
) Route
*route
= NULL
;
1041 r
= route_new_static(network
, filename
, section_line
, &route
);
1045 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1046 "Failed to allocate route, ignoring assignment: %m");
1050 r
= config_parse_tristate(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
,
1051 &route
->gateway_onlink
, network
);
1059 int config_parse_route_nexthop(
1061 const char *filename
,
1063 const char *section
,
1064 unsigned section_line
,
1071 Network
*network
= userdata
;
1072 _cleanup_(route_unref_or_set_invalidp
) Route
*route
= NULL
;
1082 r
= route_new_static(network
, filename
, section_line
, &route
);
1086 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1087 "Failed to allocate route, ignoring assignment: %m");
1091 if (isempty(rvalue
)) {
1092 route
->nexthop_id
= 0;
1097 r
= safe_atou32(rvalue
, &id
);
1099 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue
);
1103 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue
);
1107 route
->nexthop_id
= id
;
1112 int config_parse_multipath_route(
1114 const char *filename
,
1116 const char *section
,
1117 unsigned section_line
,
1124 _cleanup_(route_nexthop_freep
) RouteNextHop
*nh
= NULL
;
1125 _cleanup_(route_unref_or_set_invalidp
) Route
*route
= NULL
;
1126 _cleanup_free_
char *word
= NULL
;
1127 Network
*network
= userdata
;
1138 r
= route_new_static(network
, filename
, section_line
, &route
);
1142 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1143 "Failed to allocate route, ignoring assignment: %m");
1147 if (isempty(rvalue
)) {
1148 route
->nexthops
= ordered_set_free(route
->nexthops
);
1153 nh
= new0(RouteNextHop
, 1);
1158 r
= extract_first_word(&p
, &word
, NULL
, 0);
1162 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1163 "Invalid multipath route option, ignoring assignment: %s", rvalue
);
1167 dev
= strchr(word
, '@');
1171 r
= parse_ifindex(dev
);
1175 if (!ifname_valid_full(dev
, IFNAME_VALID_ALTERNATIVE
)) {
1176 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1177 "Invalid interface name '%s' in %s=, ignoring: %s", dev
, lvalue
, rvalue
);
1181 nh
->ifname
= strdup(dev
);
1187 r
= in_addr_from_string_auto(word
, &nh
->family
, &nh
->gw
);
1189 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1190 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue
);
1195 r
= safe_atou32(p
, &nh
->weight
);
1197 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1198 "Invalid multipath route weight, ignoring assignment: %s", p
);
1201 /* ip command takes weight in the range 1…255, while kernel takes the value in the
1202 * range 0…254. MultiPathRoute= setting also takes weight in the same range which ip
1203 * command uses, then networkd decreases by one and stores it to match the range which
1205 if (nh
->weight
== 0 || nh
->weight
> 256) {
1206 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1207 "Invalid multipath route weight, ignoring assignment: %s", p
);
1213 r
= ordered_set_ensure_put(&route
->nexthops
, &route_nexthop_hash_ops
, nh
);
1217 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1218 "Failed to store multipath route, ignoring assignment: %m");