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
);
33 set_remove(nexthop
->link
->nexthops
, nexthop
);
35 if (nexthop
->link
->manager
&& nexthop
->id
> 0)
36 hashmap_remove(nexthop
->link
->manager
->nexthops_by_id
, UINT32_TO_PTR(nexthop
->id
));
39 if (nexthop
->manager
) {
40 set_remove(nexthop
->manager
->nexthops
, nexthop
);
43 hashmap_remove(nexthop
->manager
->nexthops_by_id
, UINT32_TO_PTR(nexthop
->id
));
46 hashmap_free_free(nexthop
->group
);
48 return mfree(nexthop
);
51 DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop
, nexthop_free
);
53 static int nexthop_new(NextHop
**ret
) {
54 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
56 nexthop
= new(NextHop
, 1);
60 *nexthop
= (NextHop
) {
65 *ret
= TAKE_PTR(nexthop
);
70 static int nexthop_new_static(Network
*network
, const char *filename
, unsigned section_line
, NextHop
**ret
) {
71 _cleanup_(config_section_freep
) ConfigSection
*n
= NULL
;
72 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
78 assert(section_line
> 0);
80 r
= config_section_new(filename
, section_line
, &n
);
84 nexthop
= hashmap_get(network
->nexthops_by_section
, n
);
86 *ret
= TAKE_PTR(nexthop
);
90 r
= nexthop_new(&nexthop
);
94 nexthop
->protocol
= RTPROT_STATIC
;
95 nexthop
->network
= network
;
96 nexthop
->section
= TAKE_PTR(n
);
97 nexthop
->source
= NETWORK_CONFIG_SOURCE_STATIC
;
99 r
= hashmap_ensure_put(&network
->nexthops_by_section
, &config_section_hash_ops
, nexthop
->section
, nexthop
);
103 *ret
= TAKE_PTR(nexthop
);
107 static void nexthop_hash_func(const NextHop
*nexthop
, struct siphash
*state
) {
110 siphash24_compress(&nexthop
->protocol
, sizeof(nexthop
->protocol
), state
);
111 siphash24_compress(&nexthop
->id
, sizeof(nexthop
->id
), state
);
112 siphash24_compress(&nexthop
->blackhole
, sizeof(nexthop
->blackhole
), state
);
113 siphash24_compress(&nexthop
->family
, sizeof(nexthop
->family
), state
);
115 switch (nexthop
->family
) {
118 siphash24_compress(&nexthop
->gw
, FAMILY_ADDRESS_SIZE(nexthop
->family
), state
);
122 /* treat any other address family as AF_UNSPEC */
127 static int nexthop_compare_func(const NextHop
*a
, const NextHop
*b
) {
130 r
= CMP(a
->protocol
, b
->protocol
);
134 r
= CMP(a
->id
, b
->id
);
138 r
= CMP(a
->blackhole
, b
->blackhole
);
142 r
= CMP(a
->family
, b
->family
);
146 if (IN_SET(a
->family
, AF_INET
, AF_INET6
))
147 return memcmp(&a
->gw
, &b
->gw
, FAMILY_ADDRESS_SIZE(a
->family
));
152 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
156 nexthop_compare_func
,
159 static bool nexthop_equal(const NextHop
*a
, const NextHop
*b
) {
166 return nexthop_compare_func(a
, b
) == 0;
169 static int nexthop_dup(const NextHop
*src
, NextHop
**ret
) {
170 _cleanup_(nexthop_freep
) NextHop
*dest
= NULL
;
171 struct nexthop_grp
*nhg
;
177 dest
= newdup(NextHop
, src
, 1);
181 /* unset all pointers */
182 dest
->manager
= NULL
;
184 dest
->network
= NULL
;
185 dest
->section
= NULL
;
188 HASHMAP_FOREACH(nhg
, src
->group
) {
189 _cleanup_free_
struct nexthop_grp
*g
= NULL
;
191 g
= newdup(struct nexthop_grp
, nhg
, 1);
195 r
= hashmap_ensure_put(&dest
->group
, NULL
, UINT32_TO_PTR(g
->id
), g
);
202 *ret
= TAKE_PTR(dest
);
206 int manager_get_nexthop_by_id(Manager
*manager
, uint32_t id
, NextHop
**ret
) {
214 nh
= hashmap_get(manager
->nexthops_by_id
, UINT32_TO_PTR(id
));
223 static bool nexthop_owned_by_link(const NextHop
*nexthop
) {
224 return !nexthop
->blackhole
&& hashmap_isempty(nexthop
->group
);
227 static int nexthop_get(Manager
*manager
, Link
*link
, NextHop
*in
, NextHop
**ret
) {
233 if (nexthop_owned_by_link(in
)) {
237 nexthops
= link
->nexthops
;
242 nexthops
= manager
->nexthops
;
245 nexthop
= set_get(nexthops
, in
);
255 /* Also find nexthop configured without ID. */
256 SET_FOREACH(nexthop
, nexthops
) {
262 found
= nexthop_equal(nexthop
, in
);
276 static int nexthop_add(Manager
*manager
, Link
*link
, NextHop
*nexthop
) {
280 assert(nexthop
->id
> 0);
282 if (nexthop_owned_by_link(nexthop
)) {
285 r
= set_ensure_put(&link
->nexthops
, &nexthop_hash_ops
, nexthop
);
291 nexthop
->link
= link
;
293 manager
= link
->manager
;
297 r
= set_ensure_put(&manager
->nexthops
, &nexthop_hash_ops
, nexthop
);
303 nexthop
->manager
= manager
;
306 return hashmap_ensure_put(&manager
->nexthops_by_id
, NULL
, UINT32_TO_PTR(nexthop
->id
), nexthop
);
309 static int nexthop_acquire_id(Manager
*manager
, NextHop
*nexthop
) {
310 _cleanup_set_free_ Set
*ids
= NULL
;
320 /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
321 * nexthop_section_verify(). */
322 assert(manager
->manage_foreign_nexthops
);
324 /* Find the lowest unused ID. */
326 ORDERED_HASHMAP_FOREACH(network
, manager
->networks
) {
329 HASHMAP_FOREACH(tmp
, network
->nexthops_by_section
) {
333 r
= set_ensure_put(&ids
, NULL
, UINT32_TO_PTR(tmp
->id
));
339 for (uint32_t id
= 1; id
< UINT32_MAX
; id
++) {
340 if (manager_get_nexthop_by_id(manager
, id
, NULL
) >= 0)
342 if (set_contains(ids
, UINT32_TO_PTR(id
)))
352 static void log_nexthop_debug(const NextHop
*nexthop
, const char *str
, const Link
*link
) {
353 _cleanup_free_
char *state
= NULL
, *group
= NULL
, *flags
= NULL
;
354 struct nexthop_grp
*nhg
;
359 /* link may be NULL. */
364 (void) network_config_state_to_string_alloc(nexthop
->state
, &state
);
365 (void) route_flags_to_string_alloc(nexthop
->flags
, &flags
);
367 HASHMAP_FOREACH(nhg
, nexthop
->group
)
368 (void) strextendf_with_separator(&group
, ",", "%"PRIu32
":%"PRIu32
, nhg
->id
, nhg
->weight
+1u);
370 log_link_debug(link
, "%s %s nexthop (%s): id: %"PRIu32
", gw: %s, blackhole: %s, group: %s, flags: %s",
371 str
, strna(network_config_source_to_string(nexthop
->source
)), strna(state
),
373 IN_ADDR_TO_STRING(nexthop
->family
, &nexthop
->gw
),
374 yes_no(nexthop
->blackhole
), strna(group
), strna(flags
));
377 static int nexthop_remove_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
382 /* link may be NULL. */
384 if (link
&& IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
387 r
= sd_netlink_message_get_errno(m
);
388 if (r
< 0 && r
!= -ENOENT
)
389 log_link_message_warning_errno(link
, m
, r
, "Could not drop nexthop, ignoring");
394 static int nexthop_remove(NextHop
*nexthop
) {
395 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
401 assert(nexthop
->manager
|| (nexthop
->link
&& nexthop
->link
->manager
));
403 /* link may be NULL. */
404 link
= nexthop
->link
;
405 manager
= nexthop
->manager
?: nexthop
->link
->manager
;
407 if (nexthop
->id
== 0) {
408 log_link_debug(link
, "Cannot remove nexthop without valid ID, ignoring.");
412 log_nexthop_debug(nexthop
, "Removing", link
);
414 r
= sd_rtnl_message_new_nexthop(manager
->rtnl
, &m
, RTM_DELNEXTHOP
, AF_UNSPEC
, RTPROT_UNSPEC
);
416 return log_link_error_errno(link
, r
, "Could not create RTM_DELNEXTHOP message: %m");
418 r
= sd_netlink_message_append_u32(m
, NHA_ID
, nexthop
->id
);
420 return log_link_error_errno(link
, r
, "Could not append NHA_ID attribute: %m");
422 r
= netlink_call_async(manager
->rtnl
, NULL
, m
, nexthop_remove_handler
,
423 link
? link_netlink_destroy_callback
: NULL
, link
);
425 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
427 link_ref(link
); /* link may be NULL, link_ref() is OK with that */
429 nexthop_enter_removing(nexthop
);
433 static int nexthop_configure(NextHop
*nexthop
, Link
*link
, Request
*req
) {
434 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
438 assert(nexthop
->id
> 0);
439 assert(IN_SET(nexthop
->family
, AF_UNSPEC
, AF_INET
, AF_INET6
));
441 assert(link
->manager
);
442 assert(link
->manager
->rtnl
);
443 assert(link
->ifindex
> 0);
446 log_nexthop_debug(nexthop
, "Configuring", link
);
448 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &m
, RTM_NEWNEXTHOP
, nexthop
->family
, nexthop
->protocol
);
452 r
= sd_netlink_message_append_u32(m
, NHA_ID
, nexthop
->id
);
456 if (!hashmap_isempty(nexthop
->group
)) {
457 _cleanup_free_
struct nexthop_grp
*group
= NULL
;
458 struct nexthop_grp
*p
, *nhg
;
460 group
= new(struct nexthop_grp
, hashmap_size(nexthop
->group
));
465 HASHMAP_FOREACH(nhg
, nexthop
->group
)
468 r
= sd_netlink_message_append_data(m
, NHA_GROUP
, group
, sizeof(struct nexthop_grp
) * hashmap_size(nexthop
->group
));
472 } else if (nexthop
->blackhole
) {
473 r
= sd_netlink_message_append_flag(m
, NHA_BLACKHOLE
);
477 r
= sd_netlink_message_append_u32(m
, NHA_OIF
, link
->ifindex
);
481 if (in_addr_is_set(nexthop
->family
, &nexthop
->gw
)) {
482 r
= netlink_message_append_in_addr_union(m
, NHA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
486 r
= sd_rtnl_message_nexthop_set_flags(m
, nexthop
->flags
& RTNH_F_ONLINK
);
492 return request_call_netlink_async(link
->manager
->rtnl
, m
, req
);
495 static int static_nexthop_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Request
*req
, Link
*link
, NextHop
*nexthop
) {
501 r
= sd_netlink_message_get_errno(m
);
502 if (r
< 0 && r
!= -EEXIST
) {
503 log_link_message_warning_errno(link
, m
, r
, "Could not set nexthop");
504 link_enter_failed(link
);
508 if (link
->static_nexthop_messages
== 0) {
509 log_link_debug(link
, "Nexthops set");
510 link
->static_nexthops_configured
= true;
511 link_check_ready(link
);
517 static bool nexthop_is_ready_to_configure(Link
*link
, const NextHop
*nexthop
) {
518 struct nexthop_grp
*nhg
;
523 if (!link_is_ready_to_configure(link
, false))
526 if (nexthop_owned_by_link(nexthop
)) {
527 /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
528 * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
530 if (link
->set_flags_messages
> 0)
532 if (!FLAGS_SET(link
->flags
, IFF_UP
))
536 /* All group members must be configured first. */
537 HASHMAP_FOREACH(nhg
, nexthop
->group
) {
540 if (manager_get_nexthop_by_id(link
->manager
, nhg
->id
, &g
) < 0)
543 if (!nexthop_exists(g
))
547 if (nexthop
->id
== 0) {
550 ORDERED_SET_FOREACH(req
, link
->manager
->request_queue
) {
551 if (req
->type
!= REQUEST_TYPE_NEXTHOP
)
553 if (((NextHop
*) req
->userdata
)->id
!= 0)
554 return false; /* first configure nexthop with id. */
558 return gateway_is_ready(link
, FLAGS_SET(nexthop
->flags
, RTNH_F_ONLINK
), nexthop
->family
, &nexthop
->gw
);
561 static int nexthop_process_request(Request
*req
, Link
*link
, NextHop
*nexthop
) {
568 if (!nexthop_is_ready_to_configure(link
, nexthop
))
571 r
= nexthop_configure(nexthop
, link
, req
);
573 return log_link_warning_errno(link
, r
, "Failed to configure nexthop");
575 nexthop_enter_configuring(nexthop
);
579 static int link_request_nexthop(Link
*link
, NextHop
*nexthop
) {
585 assert(nexthop
->source
!= NETWORK_CONFIG_SOURCE_FOREIGN
);
587 if (nexthop_get(link
->manager
, link
, nexthop
, &existing
) < 0) {
588 _cleanup_(nexthop_freep
) NextHop
*tmp
= NULL
;
590 r
= nexthop_dup(nexthop
, &tmp
);
594 r
= nexthop_acquire_id(link
->manager
, tmp
);
598 r
= nexthop_add(link
->manager
, link
, tmp
);
602 existing
= TAKE_PTR(tmp
);
604 existing
->source
= nexthop
->source
;
606 log_nexthop_debug(existing
, "Requesting", link
);
607 r
= link_queue_request_safe(link
, REQUEST_TYPE_NEXTHOP
,
610 nexthop_compare_func
,
611 nexthop_process_request
,
612 &link
->static_nexthop_messages
,
613 static_nexthop_handler
,
618 nexthop_enter_requesting(existing
);
622 int link_request_static_nexthops(Link
*link
, bool only_ipv4
) {
627 assert(link
->network
);
629 link
->static_nexthops_configured
= false;
631 HASHMAP_FOREACH(nh
, link
->network
->nexthops_by_section
) {
632 if (only_ipv4
&& nh
->family
!= AF_INET
)
635 r
= link_request_nexthop(link
, nh
);
637 return log_link_warning_errno(link
, r
, "Could not request nexthop: %m");
640 if (link
->static_nexthop_messages
== 0) {
641 link
->static_nexthops_configured
= true;
642 link_check_ready(link
);
644 log_link_debug(link
, "Requesting nexthops");
645 link_set_state(link
, LINK_STATE_CONFIGURING
);
651 static void manager_mark_nexthops(Manager
*manager
, bool foreign
, const Link
*except
) {
657 /* First, mark all nexthops. */
658 SET_FOREACH(nexthop
, manager
->nexthops
) {
659 /* do not touch nexthop created by the kernel */
660 if (nexthop
->protocol
== RTPROT_KERNEL
)
663 /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */
664 if (foreign
!= (nexthop
->source
== NETWORK_CONFIG_SOURCE_FOREIGN
))
667 /* Ignore nexthops not assigned yet or already removed. */
668 if (!nexthop_exists(nexthop
))
671 nexthop_mark(nexthop
);
674 /* Then, unmark all nexthops requested by active links. */
675 HASHMAP_FOREACH(link
, manager
->links_by_index
) {
679 if (!IN_SET(link
->state
, LINK_STATE_CONFIGURING
, LINK_STATE_CONFIGURED
))
682 HASHMAP_FOREACH(nexthop
, link
->network
->nexthops_by_section
) {
685 if (nexthop_get(manager
, NULL
, nexthop
, &existing
) >= 0)
686 nexthop_unmark(existing
);
691 static int manager_drop_marked_nexthops(Manager
*manager
) {
697 SET_FOREACH(nexthop
, manager
->nexthops
) {
698 if (!nexthop_is_marked(nexthop
))
701 RET_GATHER(r
, nexthop_remove(nexthop
));
707 int link_drop_foreign_nexthops(Link
*link
) {
712 assert(link
->manager
);
713 assert(link
->network
);
715 /* First, mark all nexthops. */
716 SET_FOREACH(nexthop
, link
->nexthops
) {
717 /* do not touch nexthop created by the kernel */
718 if (nexthop
->protocol
== RTPROT_KERNEL
)
721 /* Do not remove nexthops we configured. */
722 if (nexthop
->source
!= NETWORK_CONFIG_SOURCE_FOREIGN
)
725 /* Ignore nexthops not assigned yet or already removed. */
726 if (!nexthop_exists(nexthop
))
729 nexthop_mark(nexthop
);
732 /* Then, unmark all nexthops requested by active links. */
733 HASHMAP_FOREACH(nexthop
, link
->network
->nexthops_by_section
) {
736 if (nexthop_get(NULL
, link
, nexthop
, &existing
) >= 0)
737 nexthop_unmark(existing
);
740 /* Finally, remove all marked rules. */
741 SET_FOREACH(nexthop
, link
->nexthops
) {
742 if (!nexthop_is_marked(nexthop
))
745 RET_GATHER(r
, nexthop_remove(nexthop
));
748 manager_mark_nexthops(link
->manager
, /* foreign = */ true, NULL
);
750 return RET_GATHER(r
, manager_drop_marked_nexthops(link
->manager
));
753 int link_drop_managed_nexthops(Link
*link
) {
758 assert(link
->manager
);
760 SET_FOREACH(nexthop
, link
->nexthops
) {
761 /* do not touch nexthop created by the kernel */
762 if (nexthop
->protocol
== RTPROT_KERNEL
)
765 /* Do not touch addresses managed by kernel or other tools. */
766 if (nexthop
->source
== NETWORK_CONFIG_SOURCE_FOREIGN
)
769 /* Ignore nexthops not assigned yet or already removing. */
770 if (!nexthop_exists(nexthop
))
773 RET_GATHER(r
, nexthop_remove(nexthop
));
776 manager_mark_nexthops(link
->manager
, /* foreign = */ false, link
);
778 return RET_GATHER(r
, manager_drop_marked_nexthops(link
->manager
));
781 void link_foreignize_nexthops(Link
*link
) {
786 SET_FOREACH(nexthop
, link
->nexthops
)
787 nexthop
->source
= NETWORK_CONFIG_SOURCE_FOREIGN
;
789 manager_mark_nexthops(link
->manager
, /* foreign = */ false, link
);
791 SET_FOREACH(nexthop
, link
->manager
->nexthops
) {
792 if (!nexthop_is_marked(nexthop
))
795 nexthop
->source
= NETWORK_CONFIG_SOURCE_FOREIGN
;
799 static int nexthop_update_group(NextHop
*nexthop
, const struct nexthop_grp
*group
, size_t size
) {
800 _cleanup_hashmap_free_free_ Hashmap
*h
= NULL
;
805 assert(group
|| size
== 0);
807 if (size
== 0 || size
% sizeof(struct nexthop_grp
) != 0)
808 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
809 "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
811 if ((uintptr_t) group
% alignof(struct nexthop_grp
) != 0)
812 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
813 "rtnl: received nexthop message with invalid alignment, ignoring.");
815 n_group
= size
/ sizeof(struct nexthop_grp
);
816 for (size_t i
= 0; i
< n_group
; i
++) {
817 _cleanup_free_
struct nexthop_grp
*nhg
= NULL
;
819 if (group
[i
].id
== 0) {
820 log_debug("rtnl: received nexthop message with invalid ID in group, ignoring.");
824 if (group
[i
].weight
> 254) {
825 log_debug("rtnl: received nexthop message with invalid weight in group, ignoring.");
829 nhg
= newdup(struct nexthop_grp
, group
+ i
, 1);
833 r
= hashmap_ensure_put(&h
, NULL
, UINT32_TO_PTR(nhg
->id
), nhg
);
837 log_debug_errno(r
, "Failed to store nexthop group, ignoring: %m");
844 hashmap_free_free(nexthop
->group
);
845 nexthop
->group
= TAKE_PTR(h
);
849 int manager_rtnl_process_nexthop(sd_netlink
*rtnl
, sd_netlink_message
*message
, Manager
*m
) {
850 _cleanup_(nexthop_freep
) NextHop
*tmp
= NULL
;
851 _cleanup_free_
void *raw_group
= NULL
;
852 NextHop
*nexthop
= NULL
;
853 size_t raw_group_size
;
863 if (sd_netlink_message_is_error(message
)) {
864 r
= sd_netlink_message_get_errno(message
);
866 log_message_warning_errno(message
, r
, "rtnl: failed to receive rule message, ignoring");
871 r
= sd_netlink_message_get_type(message
, &type
);
873 log_warning_errno(r
, "rtnl: could not get message type, ignoring: %m");
875 } else if (!IN_SET(type
, RTM_NEWNEXTHOP
, RTM_DELNEXTHOP
)) {
876 log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type
);
880 r
= sd_netlink_message_read_u32(message
, NHA_OIF
, &ifindex
);
881 if (r
< 0 && r
!= -ENODATA
) {
882 log_warning_errno(r
, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
886 log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32
", ignoring.", ifindex
);
890 r
= link_get_by_index(m
, ifindex
, &link
);
893 log_warning("rtnl: received nexthop message for link (%"PRIu32
") we do not know about, ignoring", ifindex
);
898 r
= nexthop_new(&tmp
);
902 r
= sd_rtnl_message_get_family(message
, &tmp
->family
);
904 log_link_warning_errno(link
, r
, "rtnl: could not get nexthop family, ignoring: %m");
906 } else if (!IN_SET(tmp
->family
, AF_UNSPEC
, AF_INET
, AF_INET6
)) {
907 log_link_debug(link
, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp
->family
);
911 r
= sd_rtnl_message_nexthop_get_protocol(message
, &tmp
->protocol
);
913 log_link_warning_errno(link
, r
, "rtnl: could not get nexthop protocol, ignoring: %m");
917 r
= sd_rtnl_message_nexthop_get_flags(message
, &tmp
->flags
);
919 log_link_warning_errno(link
, r
, "rtnl: could not get nexthop flags, ignoring: %m");
923 r
= sd_netlink_message_read_data(message
, NHA_GROUP
, &raw_group_size
, &raw_group
);
924 if (r
< 0 && r
!= -ENODATA
) {
925 log_link_warning_errno(link
, r
, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
928 r
= nexthop_update_group(tmp
, raw_group
, raw_group_size
);
933 if (tmp
->family
!= AF_UNSPEC
) {
934 r
= netlink_message_read_in_addr_union(message
, NHA_GATEWAY
, tmp
->family
, &tmp
->gw
);
935 if (r
< 0 && r
!= -ENODATA
) {
936 log_link_warning_errno(link
, r
, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
941 r
= sd_netlink_message_has_flag(message
, NHA_BLACKHOLE
);
943 log_link_warning_errno(link
, r
, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
948 r
= sd_netlink_message_read_u32(message
, NHA_ID
, &tmp
->id
);
950 log_link_warning_errno(link
, r
, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
953 log_link_warning_errno(link
, r
, "rtnl: could not get NHA_ID attribute, ignoring: %m");
955 } else if (tmp
->id
== 0) {
956 log_link_warning(link
, "rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
960 /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
961 * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
962 if (!nexthop_owned_by_link(tmp
))
965 (void) nexthop_get(m
, link
, tmp
, &nexthop
);
970 nexthop
->flags
= tmp
->flags
;
971 nexthop_enter_configured(nexthop
);
972 log_nexthop_debug(tmp
, "Received remembered", link
);
974 nexthop_enter_configured(tmp
);
975 log_nexthop_debug(tmp
, "Remembering", link
);
977 r
= nexthop_add(m
, link
, tmp
);
979 log_link_warning_errno(link
, r
, "Could not remember foreign nexthop, ignoring: %m");
989 nexthop_enter_removed(nexthop
);
990 if (nexthop
->state
== 0) {
991 log_nexthop_debug(nexthop
, "Forgetting", link
);
992 nexthop_free(nexthop
);
994 log_nexthop_debug(nexthop
, "Removed", link
);
996 log_nexthop_debug(tmp
, "Kernel removed unknown", link
);
1000 assert_not_reached();
1006 static int nexthop_section_verify(NextHop
*nh
) {
1007 if (section_is_invalid(nh
->section
))
1010 if (!nh
->network
->manager
->manage_foreign_nexthops
&& nh
->id
== 0)
1011 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1012 "%s: [NextHop] section without specifying Id= is not supported "
1013 "if ManageForeignNextHops=no is set in networkd.conf. "
1014 "Ignoring [NextHop] section from line %u.",
1015 nh
->section
->filename
, nh
->section
->line
);
1017 if (!hashmap_isempty(nh
->group
)) {
1018 if (in_addr_is_set(nh
->family
, &nh
->gw
))
1019 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1020 "%s: nexthop group cannot have gateway address. "
1021 "Ignoring [NextHop] section from line %u.",
1022 nh
->section
->filename
, nh
->section
->line
);
1024 if (nh
->family
!= AF_UNSPEC
)
1025 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1026 "%s: nexthop group cannot have Family= setting. "
1027 "Ignoring [NextHop] section from line %u.",
1028 nh
->section
->filename
, nh
->section
->line
);
1030 if (nh
->blackhole
&& in_addr_is_set(nh
->family
, &nh
->gw
))
1031 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1032 "%s: nexthop group cannot be a blackhole. "
1033 "Ignoring [NextHop] section from line %u.",
1034 nh
->section
->filename
, nh
->section
->line
);
1035 } else if (nh
->family
== AF_UNSPEC
)
1036 /* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
1037 nh
->family
= AF_INET
;
1039 if (nh
->blackhole
&& in_addr_is_set(nh
->family
, &nh
->gw
))
1040 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
),
1041 "%s: blackhole nexthop cannot have gateway address. "
1042 "Ignoring [NextHop] section from line %u.",
1043 nh
->section
->filename
, nh
->section
->line
);
1045 if (nh
->onlink
< 0 && in_addr_is_set(nh
->family
, &nh
->gw
) &&
1046 ordered_hashmap_isempty(nh
->network
->addresses_by_section
)) {
1047 /* If no address is configured, in most cases the gateway cannot be reachable.
1048 * TODO: we may need to improve the condition above. */
1049 log_warning("%s: Gateway= without static address configured. "
1050 "Enabling OnLink= option.",
1051 nh
->section
->filename
);
1055 if (nh
->onlink
>= 0)
1056 SET_FLAG(nh
->flags
, RTNH_F_ONLINK
, nh
->onlink
);
1061 void network_drop_invalid_nexthops(Network
*network
) {
1066 HASHMAP_FOREACH(nh
, network
->nexthops_by_section
)
1067 if (nexthop_section_verify(nh
) < 0)
1071 int config_parse_nexthop_id(
1073 const char *filename
,
1075 const char *section
,
1076 unsigned section_line
,
1083 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1084 Network
*network
= userdata
;
1094 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1098 if (isempty(rvalue
)) {
1104 r
= safe_atou32(rvalue
, &id
);
1106 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1107 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue
);
1111 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1112 "Invalid nexthop id \"%s\", ignoring assignment: %m", rvalue
);
1121 int config_parse_nexthop_gateway(
1123 const char *filename
,
1125 const char *section
,
1126 unsigned section_line
,
1133 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1134 Network
*network
= userdata
;
1143 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1147 if (isempty(rvalue
)) {
1148 n
->family
= AF_UNSPEC
;
1149 n
->gw
= IN_ADDR_NULL
;
1155 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->gw
);
1157 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1158 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);
1166 int config_parse_nexthop_family(
1168 const char *filename
,
1170 const char *section
,
1171 unsigned section_line
,
1178 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1179 Network
*network
= userdata
;
1189 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1193 if (isempty(rvalue
) &&
1194 !in_addr_is_set(n
->family
, &n
->gw
)) {
1195 /* Accept an empty string only when Gateway= is null or not specified. */
1196 n
->family
= AF_UNSPEC
;
1201 a
= nexthop_address_family_from_string(rvalue
);
1203 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1204 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);
1208 if (in_addr_is_set(n
->family
, &n
->gw
) &&
1209 ((a
== ADDRESS_FAMILY_IPV4
&& n
->family
== AF_INET6
) ||
1210 (a
== ADDRESS_FAMILY_IPV6
&& n
->family
== AF_INET
))) {
1211 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1212 "Specified family '%s' conflicts with the family of the previously specified Gateway=, "
1213 "ignoring assignment.", rvalue
);
1218 case ADDRESS_FAMILY_IPV4
:
1219 n
->family
= AF_INET
;
1221 case ADDRESS_FAMILY_IPV6
:
1222 n
->family
= AF_INET6
;
1225 assert_not_reached();
1232 int config_parse_nexthop_onlink(
1234 const char *filename
,
1236 const char *section
,
1237 unsigned section_line
,
1244 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1245 Network
*network
= userdata
;
1254 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1258 r
= parse_tristate(rvalue
, &n
->onlink
);
1260 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1261 "Failed to parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
1269 int config_parse_nexthop_blackhole(
1271 const char *filename
,
1273 const char *section
,
1274 unsigned section_line
,
1281 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1282 Network
*network
= userdata
;
1291 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1295 r
= parse_boolean(rvalue
);
1297 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1298 "Failed to parse %s=, ignoring assignment: %s", lvalue
, rvalue
);
1308 int config_parse_nexthop_group(
1310 const char *filename
,
1312 const char *section
,
1313 unsigned section_line
,
1320 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
1321 Network
*network
= userdata
;
1330 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
1334 if (isempty(rvalue
)) {
1335 n
->group
= hashmap_free_free(n
->group
);
1340 for (const char *p
= rvalue
;;) {
1341 _cleanup_free_
struct nexthop_grp
*nhg
= NULL
;
1342 _cleanup_free_
char *word
= NULL
;
1346 r
= extract_first_word(&p
, &word
, NULL
, 0);
1350 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1351 "Invalid %s=, ignoring assignment: %s", lvalue
, rvalue
);
1357 nhg
= new0(struct nexthop_grp
, 1);
1361 sep
= strchr(word
, ':');
1364 r
= safe_atou32(sep
, &w
);
1366 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1367 "Failed to parse weight for nexthop group, ignoring assignment: %s:%s",
1371 if (w
== 0 || w
> 256) {
1372 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1373 "Invalid weight for nexthop group, ignoring assignment: %s:%s",
1377 /* See comments in config_parse_multipath_route(). */
1378 nhg
->weight
= w
- 1;
1381 r
= safe_atou32(word
, &nhg
->id
);
1383 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1384 "Failed to parse nexthop ID in %s=, ignoring assignment: %s%s%s",
1385 lvalue
, word
, sep
? ":" : "", strempty(sep
));
1389 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1390 "Nexthop ID in %s= must be positive, ignoring assignment: %s%s%s",
1391 lvalue
, word
, sep
? ":" : "", strempty(sep
));
1395 r
= hashmap_ensure_put(&n
->group
, NULL
, UINT32_TO_PTR(nhg
->id
), nhg
);
1399 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1400 "Nexthop ID %"PRIu32
" is specified multiple times in %s=, ignoring assignment: %s%s%s",
1401 nhg
->id
, lvalue
, word
, sep
? ":" : "", strempty(sep
));