1 /* SPDX-License-Identifier: LGPL-2.1-or-later
2 * Copyright © 2019 VMware, Inc.
6 #include <linux/nexthop.h>
8 #include "alloc-util.h"
9 #include "netlink-util.h"
10 #include "networkd-link.h"
11 #include "networkd-manager.h"
12 #include "networkd-network.h"
13 #include "networkd-nexthop.h"
14 #include "networkd-queue.h"
15 #include "networkd-route-util.h"
16 #include "parse-util.h"
18 #include "stdio-util.h"
19 #include "string-util.h"
21 NextHop
*nexthop_free(NextHop
*nexthop
) {
25 if (nexthop
->network
) {
26 assert(nexthop
->section
);
27 hashmap_remove(nexthop
->network
->nexthops_by_section
, nexthop
->section
);
30 config_section_free(nexthop
->section
);
32 if (nexthop
->manager
) {
33 assert(nexthop
->id
> 0);
34 hashmap_remove(nexthop
->manager
->nexthops_by_id
, UINT32_TO_PTR(nexthop
->id
));
37 hashmap_free_free(nexthop
->group
);
39 return mfree(nexthop
);
42 DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop
, nexthop_free
);
44 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
52 static int nexthop_new(NextHop
**ret
) {
53 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
55 nexthop
= new(NextHop
, 1);
59 *nexthop
= (NextHop
) {
64 *ret
= TAKE_PTR(nexthop
);
69 static int nexthop_new_static(Network
*network
, const char *filename
, unsigned section_line
, NextHop
**ret
) {
70 _cleanup_(config_section_freep
) ConfigSection
*n
= NULL
;
71 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
77 assert(section_line
> 0);
79 r
= config_section_new(filename
, section_line
, &n
);
83 nexthop
= hashmap_get(network
->nexthops_by_section
, n
);
85 *ret
= TAKE_PTR(nexthop
);
89 r
= nexthop_new(&nexthop
);
93 nexthop
->protocol
= RTPROT_STATIC
;
94 nexthop
->network
= network
;
95 nexthop
->section
= TAKE_PTR(n
);
96 nexthop
->source
= NETWORK_CONFIG_SOURCE_STATIC
;
98 r
= hashmap_ensure_put(&network
->nexthops_by_section
, &config_section_hash_ops
, nexthop
->section
, nexthop
);
102 *ret
= TAKE_PTR(nexthop
);
106 static void nexthop_hash_func(const NextHop
*nexthop
, struct siphash
*state
) {
110 siphash24_compress(&nexthop
->id
, sizeof(nexthop
->id
), state
);
113 static int nexthop_compare_func(const NextHop
*a
, const NextHop
*b
) {
117 return CMP(a
->id
, b
->id
);
120 static int nexthop_compare_full(const NextHop
*a
, const NextHop
*b
) {
126 /* This compares detailed configs, except for ID and ifindex. */
128 r
= CMP(a
->protocol
, b
->protocol
);
132 r
= CMP(a
->flags
, b
->flags
);
136 r
= CMP(hashmap_size(a
->group
), hashmap_size(b
->group
));
140 if (!hashmap_isempty(a
->group
)) {
141 struct nexthop_grp
*ga
;
143 HASHMAP_FOREACH(ga
, a
->group
) {
144 struct nexthop_grp
*gb
;
146 gb
= hashmap_get(b
->group
, UINT32_TO_PTR(ga
->id
));
150 r
= CMP(ga
->weight
, gb
->weight
);
156 r
= CMP(a
->blackhole
, b
->blackhole
);
160 r
= CMP(a
->family
, b
->family
);
164 if (IN_SET(a
->family
, AF_INET
, AF_INET6
)) {
165 r
= memcmp(&a
->gw
, &b
->gw
, FAMILY_ADDRESS_SIZE(a
->family
));
173 static int nexthop_dup(const NextHop
*src
, NextHop
**ret
) {
174 _cleanup_(nexthop_freep
) NextHop
*dest
= NULL
;
175 struct nexthop_grp
*nhg
;
181 dest
= newdup(NextHop
, src
, 1);
185 /* unset all pointers */
186 dest
->manager
= NULL
;
187 dest
->network
= NULL
;
188 dest
->section
= NULL
;
191 HASHMAP_FOREACH(nhg
, src
->group
) {
192 _cleanup_free_
struct nexthop_grp
*g
= NULL
;
194 g
= newdup(struct nexthop_grp
, nhg
, 1);
198 r
= hashmap_ensure_put(&dest
->group
, NULL
, UINT32_TO_PTR(g
->id
), g
);
205 *ret
= TAKE_PTR(dest
);
209 static bool nexthop_bound_to_link(const NextHop
*nexthop
) {
211 return !nexthop
->blackhole
&& hashmap_isempty(nexthop
->group
);
214 int nexthop_get_by_id(Manager
*manager
, uint32_t id
, NextHop
**ret
) {
222 nh
= hashmap_get(manager
->nexthops_by_id
, UINT32_TO_PTR(id
));
231 static int nexthop_get(Link
*link
, const NextHop
*in
, NextHop
**ret
) {
236 assert(link
->manager
);
240 return nexthop_get_by_id(link
->manager
, in
->id
, ret
);
242 /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
243 * nexthop_section_verify(). */
244 assert(link
->manager
->manage_foreign_nexthops
);
246 ifindex
= nexthop_bound_to_link(in
) ? link
->ifindex
: 0;
248 HASHMAP_FOREACH(nexthop
, link
->manager
->nexthops_by_id
) {
249 if (nexthop
->ifindex
!= ifindex
)
251 if (nexthop_compare_full(nexthop
, in
) != 0)
262 static int nexthop_get_request_by_id(Manager
*manager
, uint32_t id
, Request
**ret
) {
267 req
= ordered_set_get(
268 manager
->request_queue
,
270 .type
= REQUEST_TYPE_NEXTHOP
,
271 .userdata
= (void*) &(const NextHop
) { .id
= id
},
272 .hash_func
= (hash_func_t
) nexthop_hash_func
,
273 .compare_func
= (compare_func_t
) nexthop_compare_func
,
283 static int nexthop_get_request(Link
*link
, const NextHop
*in
, Request
**ret
) {
288 assert(link
->manager
);
292 return nexthop_get_request_by_id(link
->manager
, in
->id
, ret
);
294 /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
295 * nexthop_section_verify(). */
296 assert(link
->manager
->manage_foreign_nexthops
);
298 ifindex
= nexthop_bound_to_link(in
) ? link
->ifindex
: 0;
300 ORDERED_SET_FOREACH(req
, link
->manager
->request_queue
) {
301 if (req
->type
!= REQUEST_TYPE_NEXTHOP
)
304 NextHop
*nexthop
= ASSERT_PTR(req
->userdata
);
305 if (nexthop
->ifindex
!= ifindex
)
307 if (nexthop_compare_full(nexthop
, in
) != 0)
318 static int nexthop_add_new(Manager
*manager
, uint32_t id
, NextHop
**ret
) {
319 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
325 r
= nexthop_new(&nexthop
);
331 r
= hashmap_ensure_put(&manager
->nexthops_by_id
, &nexthop_hash_ops
, UINT32_TO_PTR(nexthop
->id
), nexthop
);
337 nexthop
->manager
= manager
;
346 static int nexthop_acquire_id(Manager
*manager
, NextHop
*nexthop
) {
347 _cleanup_set_free_ Set
*ids
= NULL
;
357 /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
358 * nexthop_section_verify(). */
359 assert(manager
->manage_foreign_nexthops
);
361 /* Find the lowest unused ID. */
363 ORDERED_HASHMAP_FOREACH(network
, manager
->networks
) {
366 HASHMAP_FOREACH(tmp
, network
->nexthops_by_section
) {
370 r
= set_ensure_put(&ids
, NULL
, UINT32_TO_PTR(tmp
->id
));
376 for (uint32_t id
= 1; id
< UINT32_MAX
; id
++) {
377 if (nexthop_get_by_id(manager
, id
, NULL
) >= 0)
379 if (nexthop_get_request_by_id(manager
, id
, NULL
) >= 0)
381 if (set_contains(ids
, UINT32_TO_PTR(id
)))
391 static void log_nexthop_debug(const NextHop
*nexthop
, const char *str
, Manager
*manager
) {
392 _cleanup_free_
char *state
= NULL
, *group
= NULL
, *flags
= NULL
;
393 struct nexthop_grp
*nhg
;
403 (void) link_get_by_index(manager
, nexthop
->ifindex
, &link
);
404 (void) network_config_state_to_string_alloc(nexthop
->state
, &state
);
405 (void) route_flags_to_string_alloc(nexthop
->flags
, &flags
);
407 HASHMAP_FOREACH(nhg
, nexthop
->group
)
408 (void) strextendf_with_separator(&group
, ",", "%"PRIu32
":%"PRIu32
, nhg
->id
, nhg
->weight
+1u);
410 log_link_debug(link
, "%s %s nexthop (%s): id: %"PRIu32
", gw: %s, blackhole: %s, group: %s, flags: %s",
411 str
, strna(network_config_source_to_string(nexthop
->source
)), strna(state
),
413 IN_ADDR_TO_STRING(nexthop
->family
, &nexthop
->gw
),
414 yes_no(nexthop
->blackhole
), strna(group
), strna(flags
));
417 static int nexthop_remove_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
422 /* link may be NULL. */
424 if (link
&& IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
427 r
= sd_netlink_message_get_errno(m
);
428 if (r
< 0 && r
!= -ENOENT
)
429 log_link_message_warning_errno(link
, m
, r
, "Could not drop nexthop, ignoring");
434 static int nexthop_remove(NextHop
*nexthop
) {
435 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
442 assert(nexthop
->id
> 0);
444 manager
= ASSERT_PTR(nexthop
->manager
);
446 /* link may be NULL. */
447 (void) link_get_by_index(manager
, nexthop
->ifindex
, &link
);
449 log_nexthop_debug(nexthop
, "Removing", manager
);
451 r
= sd_rtnl_message_new_nexthop(manager
->rtnl
, &m
, RTM_DELNEXTHOP
, AF_UNSPEC
, RTPROT_UNSPEC
);
453 return log_link_error_errno(link
, r
, "Could not create RTM_DELNEXTHOP message: %m");
455 r
= sd_netlink_message_append_u32(m
, NHA_ID
, nexthop
->id
);
457 return log_link_error_errno(link
, r
, "Could not append NHA_ID attribute: %m");
459 r
= netlink_call_async(manager
->rtnl
, NULL
, m
, nexthop_remove_handler
,
460 link
? link_netlink_destroy_callback
: NULL
, link
);
462 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
464 link_ref(link
); /* link may be NULL, link_ref() is OK with that */
466 nexthop_enter_removing(nexthop
);
467 if (nexthop_get_request_by_id(manager
, nexthop
->id
, &req
) >= 0)
468 nexthop_enter_removing(req
->userdata
);
473 static int nexthop_configure(NextHop
*nexthop
, Link
*link
, Request
*req
) {
474 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
478 assert(nexthop
->id
> 0);
479 assert(IN_SET(nexthop
->family
, AF_UNSPEC
, AF_INET
, AF_INET6
));
481 assert(link
->manager
);
482 assert(link
->manager
->rtnl
);
483 assert(link
->ifindex
> 0);
486 log_nexthop_debug(nexthop
, "Configuring", link
->manager
);
488 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &m
, RTM_NEWNEXTHOP
, nexthop
->family
, nexthop
->protocol
);
492 r
= sd_netlink_message_append_u32(m
, NHA_ID
, nexthop
->id
);
496 if (!hashmap_isempty(nexthop
->group
)) {
497 _cleanup_free_
struct nexthop_grp
*group
= NULL
;
498 struct nexthop_grp
*p
, *nhg
;
500 group
= new(struct nexthop_grp
, hashmap_size(nexthop
->group
));
505 HASHMAP_FOREACH(nhg
, nexthop
->group
)
508 r
= sd_netlink_message_append_data(m
, NHA_GROUP
, group
, sizeof(struct nexthop_grp
) * hashmap_size(nexthop
->group
));
512 } else if (nexthop
->blackhole
) {
513 r
= sd_netlink_message_append_flag(m
, NHA_BLACKHOLE
);
517 assert(nexthop
->ifindex
== link
->ifindex
);
519 r
= sd_netlink_message_append_u32(m
, NHA_OIF
, nexthop
->ifindex
);
523 if (in_addr_is_set(nexthop
->family
, &nexthop
->gw
)) {
524 r
= netlink_message_append_in_addr_union(m
, NHA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
528 r
= sd_rtnl_message_nexthop_set_flags(m
, nexthop
->flags
& RTNH_F_ONLINK
);
534 return request_call_netlink_async(link
->manager
->rtnl
, m
, req
);
537 static int static_nexthop_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Request
*req
, Link
*link
, NextHop
*nexthop
) {
543 r
= sd_netlink_message_get_errno(m
);
544 if (r
< 0 && r
!= -EEXIST
) {
545 log_link_message_warning_errno(link
, m
, r
, "Could not set nexthop");
546 link_enter_failed(link
);
550 if (link
->static_nexthop_messages
== 0) {
551 log_link_debug(link
, "Nexthops set");
552 link
->static_nexthops_configured
= true;
553 link_check_ready(link
);
559 static bool nexthop_is_ready_to_configure(Link
*link
, const NextHop
*nexthop
) {
560 struct nexthop_grp
*nhg
;
564 assert(nexthop
->id
> 0);
566 if (!link_is_ready_to_configure(link
, false))
569 if (nexthop_bound_to_link(nexthop
)) {
570 assert(nexthop
->ifindex
== link
->ifindex
);
572 /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
573 * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
575 if (link
->set_flags_messages
> 0)
577 if (!FLAGS_SET(link
->flags
, IFF_UP
))
581 /* All group members must be configured first. */
582 HASHMAP_FOREACH(nhg
, nexthop
->group
) {
585 if (nexthop_get_by_id(link
->manager
, nhg
->id
, &g
) < 0)
588 if (!nexthop_exists(g
))
592 return gateway_is_ready(link
, FLAGS_SET(nexthop
->flags
, RTNH_F_ONLINK
), nexthop
->family
, &nexthop
->gw
);
595 static int nexthop_process_request(Request
*req
, Link
*link
, NextHop
*nexthop
) {
601 assert(link
->manager
);
604 if (!nexthop_is_ready_to_configure(link
, nexthop
))
607 r
= nexthop_configure(nexthop
, link
, req
);
609 return log_link_warning_errno(link
, r
, "Failed to configure nexthop");
611 nexthop_enter_configuring(nexthop
);
612 if (nexthop_get_by_id(link
->manager
, nexthop
->id
, &existing
) >= 0)
613 nexthop_enter_configuring(existing
);
618 static int link_request_nexthop(Link
*link
, const NextHop
*nexthop
) {
619 _cleanup_(nexthop_freep
) NextHop
*tmp
= NULL
;
620 NextHop
*existing
= NULL
;
624 assert(link
->manager
);
626 assert(nexthop
->source
!= NETWORK_CONFIG_SOURCE_FOREIGN
);
628 if (nexthop_get_request(link
, nexthop
, NULL
) >= 0)
629 return 0; /* already requested, skipping. */
631 r
= nexthop_dup(nexthop
, &tmp
);
635 if (nexthop_get(link
, nexthop
, &existing
) < 0) {
636 r
= nexthop_acquire_id(link
->manager
, tmp
);
641 assert(tmp
->id
== 0 || tmp
->id
== existing
->id
);
642 tmp
->id
= existing
->id
;
644 /* Copy state for logging below. */
645 tmp
->state
= existing
->state
;
648 if (nexthop_bound_to_link(tmp
))
649 tmp
->ifindex
= link
->ifindex
;
651 log_nexthop_debug(tmp
, "Requesting", link
->manager
);
652 r
= link_queue_request_safe(link
, REQUEST_TYPE_NEXTHOP
,
656 nexthop_compare_func
,
657 nexthop_process_request
,
658 &link
->static_nexthop_messages
,
659 static_nexthop_handler
,
664 nexthop_enter_requesting(tmp
);
666 nexthop_enter_requesting(existing
);
672 int link_request_static_nexthops(Link
*link
, bool only_ipv4
) {
677 assert(link
->network
);
679 link
->static_nexthops_configured
= false;
681 HASHMAP_FOREACH(nh
, link
->network
->nexthops_by_section
) {
682 if (only_ipv4
&& nh
->family
!= AF_INET
)
685 r
= link_request_nexthop(link
, nh
);
687 return log_link_warning_errno(link
, r
, "Could not request nexthop: %m");
690 if (link
->static_nexthop_messages
== 0) {
691 link
->static_nexthops_configured
= true;
692 link_check_ready(link
);
694 log_link_debug(link
, "Requesting nexthops");
695 link_set_state(link
, LINK_STATE_CONFIGURING
);
701 static bool nexthop_can_update(const NextHop
*assigned_nexthop
, const NextHop
*requested_nexthop
) {
702 assert(assigned_nexthop
);
703 assert(assigned_nexthop
->manager
);
704 assert(requested_nexthop
);
705 assert(requested_nexthop
->network
);
707 /* A group nexthop cannot be replaced with a non-group nexthop, and vice versa.
708 * See replace_nexthop_grp() and replace_nexthop_single() in net/ipv4/nexthop.c of the kernel. */
709 if (hashmap_isempty(assigned_nexthop
->group
) != hashmap_isempty(requested_nexthop
->group
))
712 /* There are several more conditions if we can replace a group nexthop, e.g. hash threshold and
713 * resilience. But, currently we do not support to modify that. Let's add checks for them in the
714 * future when we support to configure them.*/
716 /* When a nexthop is replaced with a blackhole nexthop, and a group nexthop has multiple nexthops
717 * including this nexthop, then the kernel refuses to replace the existing nexthop.
718 * So, here, for simplicity, let's unconditionally refuse to replace a non-blackhole nexthop with
719 * a blackhole nexthop. See replace_nexthop() in net/ipv4/nexthop.c of the kernel. */
720 if (!assigned_nexthop
->blackhole
&& requested_nexthop
->blackhole
)
726 static void link_mark_nexthops(Link
*link
, bool foreign
) {
731 assert(link
->manager
);
733 /* First, mark all nexthops. */
734 HASHMAP_FOREACH(nexthop
, link
->manager
->nexthops_by_id
) {
735 /* do not touch nexthop created by the kernel */
736 if (nexthop
->protocol
== RTPROT_KERNEL
)
739 /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */
740 if (foreign
!= (nexthop
->source
== NETWORK_CONFIG_SOURCE_FOREIGN
))
743 /* Ignore nexthops not assigned yet or already removed. */
744 if (!nexthop_exists(nexthop
))
747 /* Ignore nexthops bound to other links. */
748 if (nexthop
->ifindex
> 0 && nexthop
->ifindex
!= link
->ifindex
)
751 nexthop_mark(nexthop
);
754 /* Then, unmark all nexthops requested by active links. */
755 HASHMAP_FOREACH(other
, link
->manager
->links_by_index
) {
756 if (!foreign
&& other
== link
)
759 if (!IN_SET(other
->state
, LINK_STATE_CONFIGURING
, LINK_STATE_CONFIGURED
))
762 HASHMAP_FOREACH(nexthop
, other
->network
->nexthops_by_section
) {
765 if (nexthop_get(other
, nexthop
, &existing
) < 0)
768 if (!nexthop_can_update(existing
, nexthop
))
771 /* Found matching static configuration. Keep the existing nexthop. */
772 nexthop_unmark(existing
);
777 int link_drop_nexthops(Link
*link
, bool foreign
) {
782 assert(link
->manager
);
784 link_mark_nexthops(link
, foreign
);
786 HASHMAP_FOREACH(nexthop
, link
->manager
->nexthops_by_id
) {
787 if (!nexthop_is_marked(nexthop
))
790 RET_GATHER(r
, nexthop_remove(nexthop
));
796 void link_foreignize_nexthops(Link
*link
) {
800 assert(link
->manager
);
802 link_mark_nexthops(link
, /* foreign = */ false);
804 HASHMAP_FOREACH(nexthop
, link
->manager
->nexthops_by_id
) {
805 if (!nexthop_is_marked(nexthop
))
808 nexthop
->source
= NETWORK_CONFIG_SOURCE_FOREIGN
;
812 static int nexthop_update_group(NextHop
*nexthop
, const struct nexthop_grp
*group
, size_t size
) {
813 _cleanup_hashmap_free_free_ Hashmap
*h
= NULL
;
818 assert(group
|| size
== 0);
820 if (size
== 0 || size
% sizeof(struct nexthop_grp
) != 0)
821 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
822 "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
824 if ((uintptr_t) group
% alignof(struct nexthop_grp
) != 0)
825 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
826 "rtnl: received nexthop message with invalid alignment, ignoring.");
828 n_group
= size
/ sizeof(struct nexthop_grp
);
829 for (size_t i
= 0; i
< n_group
; i
++) {
830 _cleanup_free_
struct nexthop_grp
*nhg
= NULL
;
832 if (group
[i
].id
== 0) {
833 log_debug("rtnl: received nexthop message with invalid ID in group, ignoring.");
837 if (group
[i
].weight
> 254) {
838 log_debug("rtnl: received nexthop message with invalid weight in group, ignoring.");
842 nhg
= newdup(struct nexthop_grp
, group
+ i
, 1);
846 r
= hashmap_ensure_put(&h
, NULL
, UINT32_TO_PTR(nhg
->id
), nhg
);
850 log_debug_errno(r
, "Failed to store nexthop group, ignoring: %m");
857 hashmap_free_free(nexthop
->group
);
858 nexthop
->group
= TAKE_PTR(h
);
862 int manager_rtnl_process_nexthop(sd_netlink
*rtnl
, sd_netlink_message
*message
, Manager
*m
) {
863 _cleanup_free_
void *raw_group
= NULL
;
864 size_t raw_group_size
;
866 uint32_t id
, ifindex
;
867 NextHop
*nexthop
= NULL
;
876 if (sd_netlink_message_is_error(message
)) {
877 r
= sd_netlink_message_get_errno(message
);
879 log_message_warning_errno(message
, r
, "rtnl: failed to receive rule message, ignoring");
884 r
= sd_netlink_message_get_type(message
, &type
);
886 log_warning_errno(r
, "rtnl: could not get message type, ignoring: %m");
888 } else if (!IN_SET(type
, RTM_NEWNEXTHOP
, RTM_DELNEXTHOP
)) {
889 log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type
);
893 r
= sd_netlink_message_read_u32(message
, NHA_ID
, &id
);
895 log_warning_errno(r
, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
898 log_warning_errno(r
, "rtnl: could not get NHA_ID attribute, ignoring: %m");
900 } else if (id
== 0) {
901 log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
905 (void) nexthop_get_by_id(m
, id
, &nexthop
);
906 (void) nexthop_get_request_by_id(m
, id
, &req
);
908 if (type
== RTM_DELNEXTHOP
) {
910 nexthop_enter_removed(nexthop
);
911 log_nexthop_debug(nexthop
, "Forgetting removed", m
);
912 nexthop_free(nexthop
);
914 log_nexthop_debug(&(const NextHop
) { .id
= id
}, "Kernel removed unknown", m
);
917 nexthop_enter_removed(req
->userdata
);
922 /* If we did not know the nexthop, then save it. */
924 r
= nexthop_add_new(m
, id
, &nexthop
);
926 log_warning_errno(r
, "Failed to add received nexthop, ignoring: %m");
933 /* Also update information that cannot be obtained through netlink notification. */
934 if (req
&& req
->waiting_reply
) {
935 NextHop
*n
= ASSERT_PTR(req
->userdata
);
937 nexthop
->source
= n
->source
;
940 r
= sd_rtnl_message_get_family(message
, &nexthop
->family
);
942 log_debug_errno(r
, "rtnl: could not get nexthop family, ignoring: %m");
944 r
= sd_rtnl_message_nexthop_get_protocol(message
, &nexthop
->protocol
);
946 log_debug_errno(r
, "rtnl: could not get nexthop protocol, ignoring: %m");
948 r
= sd_rtnl_message_nexthop_get_flags(message
, &nexthop
->flags
);
950 log_debug_errno(r
, "rtnl: could not get nexthop flags, ignoring: %m");
952 r
= sd_netlink_message_read_data(message
, NHA_GROUP
, &raw_group_size
, &raw_group
);
954 nexthop
->group
= hashmap_free_free(nexthop
->group
);
956 log_debug_errno(r
, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
958 (void) nexthop_update_group(nexthop
, raw_group
, raw_group_size
);
960 if (nexthop
->family
!= AF_UNSPEC
) {
961 r
= netlink_message_read_in_addr_union(message
, NHA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
963 nexthop
->gw
= IN_ADDR_NULL
;
965 log_debug_errno(r
, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
968 r
= sd_netlink_message_has_flag(message
, NHA_BLACKHOLE
);
970 log_debug_errno(r
, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
972 nexthop
->blackhole
= r
;
974 r
= sd_netlink_message_read_u32(message
, NHA_OIF
, &ifindex
);
976 nexthop
->ifindex
= 0;
978 log_debug_errno(r
, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
979 else if (ifindex
> INT32_MAX
)
980 log_debug_errno(r
, "rtnl: received invalid NHA_OIF attribute, ignoring: %m");
982 nexthop
->ifindex
= (int) ifindex
;
984 /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
985 * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
986 if (!nexthop_bound_to_link(nexthop
))
987 nexthop
->ifindex
= 0;
989 nexthop_enter_configured(nexthop
);
991 nexthop_enter_configured(req
->userdata
);
993 log_nexthop_debug(nexthop
, is_new
? "Remembering" : "Received remembered", m
);
997 static int nexthop_section_verify(NextHop
*nh
) {
998 if (section_is_invalid(nh
->section
))
1001 if (!nh
->network
->manager
->manage_foreign_nexthops
&& nh
->id
== 0)
1002 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1003 "%s: [NextHop] section without specifying Id= is not supported "
1004 "if ManageForeignNextHops=no is set in networkd.conf. "
1005 "Ignoring [NextHop] section from line %u.",
1006 nh
->section
->filename
, nh
->section
->line
);
1008 if (!hashmap_isempty(nh
->group
)) {
1009 if (in_addr_is_set(nh
->family
, &nh
->gw
))
1010 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1011 "%s: nexthop group cannot have gateway address. "
1012 "Ignoring [NextHop] section from line %u.",
1013 nh
->section
->filename
, nh
->section
->line
);
1015 if (nh
->family
!= AF_UNSPEC
)
1016 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1017 "%s: nexthop group cannot have Family= setting. "
1018 "Ignoring [NextHop] section from line %u.",
1019 nh
->section
->filename
, nh
->section
->line
);
1022 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1023 "%s: nexthop group cannot be a blackhole. "
1024 "Ignoring [NextHop] section from line %u.",
1025 nh
->section
->filename
, nh
->section
->line
);
1028 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1029 "%s: nexthop group cannot have on-link flag. "
1030 "Ignoring [NextHop] section from line %u.",
1031 nh
->section
->filename
, nh
->section
->line
);
1032 } else if (nh
->family
== AF_UNSPEC
)
1033 /* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
1034 nh
->family
= AF_INET
;
1036 if (nh
->blackhole
) {
1037 if (in_addr_is_set(nh
->family
, &nh
->gw
))
1038 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1039 "%s: blackhole nexthop cannot have gateway address. "
1040 "Ignoring [NextHop] section from line %u.",
1041 nh
->section
->filename
, nh
->section
->line
);
1044 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1045 "%s: blackhole nexthop cannot have on-link flag. "
1046 "Ignoring [NextHop] section from line %u.",
1047 nh
->section
->filename
, nh
->section
->line
);
1050 if (nh
->onlink
< 0 && in_addr_is_set(nh
->family
, &nh
->gw
) &&
1051 ordered_hashmap_isempty(nh
->network
->addresses_by_section
)) {
1052 /* If no address is configured, in most cases the gateway cannot be reachable.
1053 * TODO: we may need to improve the condition above. */
1054 log_warning("%s: Gateway= without static address configured. "
1055 "Enabling OnLink= option.",
1056 nh
->section
->filename
);
1060 if (nh
->onlink
>= 0)
1061 SET_FLAG(nh
->flags
, RTNH_F_ONLINK
, nh
->onlink
);
1066 void network_drop_invalid_nexthops(Network
*network
) {
1071 HASHMAP_FOREACH(nh
, network
->nexthops_by_section
)
1072 if (nexthop_section_verify(nh
) < 0)
1076 int config_parse_nexthop_id(
1078 const char *filename
,
1080 const char *section
,
1081 unsigned section_line
,
1088 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1089 Network
*network
= userdata
;
1099 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1103 if (isempty(rvalue
)) {
1109 r
= safe_atou32(rvalue
, &id
);
1111 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1112 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue
);
1116 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1117 "Invalid nexthop id \"%s\", ignoring assignment: %m", rvalue
);
1126 int config_parse_nexthop_gateway(
1128 const char *filename
,
1130 const char *section
,
1131 unsigned section_line
,
1138 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1139 Network
*network
= userdata
;
1148 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1152 if (isempty(rvalue
)) {
1153 n
->family
= AF_UNSPEC
;
1154 n
->gw
= IN_ADDR_NULL
;
1160 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->gw
);
1162 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1163 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);
1171 int config_parse_nexthop_family(
1173 const char *filename
,
1175 const char *section
,
1176 unsigned section_line
,
1183 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1184 Network
*network
= userdata
;
1194 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1198 if (isempty(rvalue
) &&
1199 !in_addr_is_set(n
->family
, &n
->gw
)) {
1200 /* Accept an empty string only when Gateway= is null or not specified. */
1201 n
->family
= AF_UNSPEC
;
1206 a
= nexthop_address_family_from_string(rvalue
);
1208 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1209 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);
1213 if (in_addr_is_set(n
->family
, &n
->gw
) &&
1214 ((a
== ADDRESS_FAMILY_IPV4
&& n
->family
== AF_INET6
) ||
1215 (a
== ADDRESS_FAMILY_IPV6
&& n
->family
== AF_INET
))) {
1216 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1217 "Specified family '%s' conflicts with the family of the previously specified Gateway=, "
1218 "ignoring assignment.", rvalue
);
1223 case ADDRESS_FAMILY_IPV4
:
1224 n
->family
= AF_INET
;
1226 case ADDRESS_FAMILY_IPV6
:
1227 n
->family
= AF_INET6
;
1230 assert_not_reached();
1237 int config_parse_nexthop_onlink(
1239 const char *filename
,
1241 const char *section
,
1242 unsigned section_line
,
1249 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1250 Network
*network
= userdata
;
1259 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1263 r
= parse_tristate(rvalue
, &n
->onlink
);
1265 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1266 "Failed to parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
1274 int config_parse_nexthop_blackhole(
1276 const char *filename
,
1278 const char *section
,
1279 unsigned section_line
,
1286 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1287 Network
*network
= userdata
;
1296 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1300 r
= parse_boolean(rvalue
);
1302 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1303 "Failed to parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
1313 int config_parse_nexthop_group(
1315 const char *filename
,
1317 const char *section
,
1318 unsigned section_line
,
1325 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1326 Network
*network
= userdata
;
1335 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1339 if (isempty(rvalue
)) {
1340 n
->group
= hashmap_free_free(n
->group
);
1345 for (const char *p
= rvalue
;;) {
1346 _cleanup_free_
struct nexthop_grp
*nhg
= NULL
;
1347 _cleanup_free_
char *word
= NULL
;
1351 r
= extract_first_word(&p
, &word
, NULL
, 0);
1355 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1356 "Invalid %s=, ignoring assignment: %s", lvalue
, rvalue
);
1362 nhg
= new0(struct nexthop_grp
, 1);
1366 sep
= strchr(word
, ':');
1369 r
= safe_atou32(sep
, &w
);
1371 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1372 "Failed to parse weight for nexthop group, ignoring assignment: %s:%s",
1376 if (w
== 0 || w
> 256) {
1377 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1378 "Invalid weight for nexthop group, ignoring assignment: %s:%s",
1382 /* See comments in config_parse_multipath_route(). */
1383 nhg
->weight
= w
- 1;
1386 r
= safe_atou32(word
, &nhg
->id
);
1388 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1389 "Failed to parse nexthop ID in %s=, ignoring assignment: %s%s%s",
1390 lvalue
, word
, sep
? ":" : "", strempty(sep
));
1394 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1395 "Nexthop ID in %s= must be positive, ignoring assignment: %s%s%s",
1396 lvalue
, word
, sep
? ":" : "", strempty(sep
));
1400 r
= hashmap_ensure_put(&n
->group
, NULL
, UINT32_TO_PTR(nhg
->id
), nhg
);
1404 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1405 "Nexthop ID %"PRIu32
" is specified multiple times in %s=, ignoring assignment: %s%s%s",
1406 nhg
->id
, lvalue
, word
, sep
? ":" : "", strempty(sep
));