1 /* SPDX-License-Identifier: LGPL-2.1-or-later
2 * Copyright © 2019 VMware, Inc.
5 #include <linux/nexthop.h>
7 #include "alloc-util.h"
8 #include "netlink-util.h"
9 #include "networkd-link.h"
10 #include "networkd-manager.h"
11 #include "networkd-network.h"
12 #include "networkd-nexthop.h"
13 #include "parse-util.h"
15 #include "string-util.h"
17 NextHop
*nexthop_free(NextHop
*nexthop
) {
21 if (nexthop
->network
) {
22 assert(nexthop
->section
);
23 hashmap_remove(nexthop
->network
->nexthops_by_section
, nexthop
->section
);
26 network_config_section_free(nexthop
->section
);
29 set_remove(nexthop
->link
->nexthops
, nexthop
);
30 set_remove(nexthop
->link
->nexthops_foreign
, nexthop
);
33 return mfree(nexthop
);
36 DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop
, nexthop_free
);
38 static int nexthop_new(NextHop
**ret
) {
39 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
41 nexthop
= new(NextHop
, 1);
45 *nexthop
= (NextHop
) {
49 *ret
= TAKE_PTR(nexthop
);
54 static int nexthop_new_static(Network
*network
, const char *filename
, unsigned section_line
, NextHop
**ret
) {
55 _cleanup_(network_config_section_freep
) NetworkConfigSection
*n
= NULL
;
56 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
62 assert(section_line
> 0);
64 r
= network_config_section_new(filename
, section_line
, &n
);
68 nexthop
= hashmap_get(network
->nexthops_by_section
, n
);
70 *ret
= TAKE_PTR(nexthop
);
74 r
= nexthop_new(&nexthop
);
78 nexthop
->protocol
= RTPROT_STATIC
;
79 nexthop
->network
= network
;
80 nexthop
->section
= TAKE_PTR(n
);
82 r
= hashmap_ensure_put(&network
->nexthops_by_section
, &network_config_hash_ops
, nexthop
->section
, nexthop
);
86 *ret
= TAKE_PTR(nexthop
);
90 static void nexthop_hash_func(const NextHop
*nexthop
, struct siphash
*state
) {
93 siphash24_compress(&nexthop
->id
, sizeof(nexthop
->id
), state
);
94 siphash24_compress(&nexthop
->family
, sizeof(nexthop
->family
), state
);
96 switch (nexthop
->family
) {
99 siphash24_compress(&nexthop
->gw
, FAMILY_ADDRESS_SIZE(nexthop
->family
), state
);
103 /* treat any other address family as AF_UNSPEC */
108 static int nexthop_compare_func(const NextHop
*a
, const NextHop
*b
) {
111 r
= CMP(a
->id
, b
->id
);
115 r
= CMP(a
->family
, b
->family
);
119 if (IN_SET(a
->family
, AF_INET
, AF_INET6
))
120 return memcmp(&a
->gw
, &b
->gw
, FAMILY_ADDRESS_SIZE(a
->family
));
125 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
129 nexthop_compare_func
,
132 static int nexthop_get(Link
*link
, NextHop
*in
, NextHop
**ret
) {
138 existing
= set_get(link
->nexthops
, in
);
145 existing
= set_get(link
->nexthops_foreign
, in
);
155 static int nexthop_add_internal(Link
*link
, Set
**nexthops
, NextHop
*in
, NextHop
**ret
) {
156 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
163 r
= nexthop_new(&nexthop
);
167 nexthop
->id
= in
->id
;
168 nexthop
->family
= in
->family
;
169 nexthop
->gw
= in
->gw
;
171 r
= set_ensure_put(nexthops
, &nexthop_hash_ops
, nexthop
);
177 nexthop
->link
= link
;
186 static int nexthop_add_foreign(Link
*link
, NextHop
*in
, NextHop
**ret
) {
187 return nexthop_add_internal(link
, &link
->nexthops_foreign
, in
, ret
);
190 static int nexthop_add(Link
*link
, NextHop
*in
, NextHop
**ret
) {
195 r
= nexthop_get(link
, in
, &nexthop
);
197 /* NextHop does not exist, create a new one */
198 r
= nexthop_add_internal(link
, &link
->nexthops
, in
, &nexthop
);
203 /* Take over a foreign nexthop */
204 r
= set_ensure_put(&link
->nexthops
, &nexthop_hash_ops
, nexthop
);
208 set_remove(link
->nexthops_foreign
, nexthop
);
210 /* NextHop exists, do nothing */
220 static int nexthop_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
224 assert(link
->nexthop_messages
> 0);
226 link
->nexthop_messages
--;
228 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
231 r
= sd_netlink_message_get_errno(m
);
232 if (r
< 0 && r
!= -EEXIST
) {
233 log_link_message_warning_errno(link
, m
, r
, "Could not set nexthop");
234 link_enter_failed(link
);
238 if (link
->nexthop_messages
== 0) {
239 log_link_debug(link
, "Nexthop set");
240 link
->static_nexthops_configured
= true;
241 link_check_ready(link
);
247 static int nexthop_configure(NextHop
*nexthop
, Link
*link
) {
248 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
252 assert(link
->manager
);
253 assert(link
->manager
->rtnl
);
254 assert(link
->ifindex
> 0);
255 assert(IN_SET(nexthop
->family
, AF_INET
, AF_INET6
));
258 _cleanup_free_
char *gw
= NULL
;
260 if (!in_addr_is_null(nexthop
->family
, &nexthop
->gw
))
261 (void) in_addr_to_string(nexthop
->family
, &nexthop
->gw
, &gw
);
263 log_link_debug(link
, "Configuring nexthop: gw: %s", strna(gw
));
266 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &req
,
267 RTM_NEWNEXTHOP
, nexthop
->family
,
270 return log_link_error_errno(link
, r
, "Could not create RTM_NEWNEXTHOP message: %m");
272 r
= sd_netlink_message_append_u32(req
, NHA_ID
, nexthop
->id
);
274 return log_link_error_errno(link
, r
, "Could not append NHA_ID attribute: %m");
276 r
= sd_netlink_message_append_u32(req
, NHA_OIF
, link
->ifindex
);
278 return log_link_error_errno(link
, r
, "Could not append NHA_OIF attribute: %m");
280 if (in_addr_is_null(nexthop
->family
, &nexthop
->gw
) == 0) {
281 r
= netlink_message_append_in_addr_union(req
, NHA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
283 return log_link_error_errno(link
, r
, "Could not append NHA_GATEWAY attribute: %m");
286 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, nexthop_handler
,
287 link_netlink_destroy_callback
, link
);
289 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
293 r
= nexthop_add(link
, nexthop
, &nexthop
);
295 return log_link_error_errno(link
, r
, "Could not add nexthop: %m");
300 int link_set_nexthop(Link
*link
) {
305 assert(link
->network
);
307 if (link
->nexthop_messages
!= 0) {
308 log_link_debug(link
, "Nexthops are configuring.");
312 link
->static_nexthops_configured
= false;
314 HASHMAP_FOREACH(nh
, link
->network
->nexthops_by_section
) {
315 r
= nexthop_configure(nh
, link
);
317 return log_link_warning_errno(link
, r
, "Could not set nexthop: %m");
319 link
->nexthop_messages
++;
322 if (link
->nexthop_messages
== 0) {
323 link
->static_nexthops_configured
= true;
324 link_check_ready(link
);
326 log_link_debug(link
, "Setting nexthop");
327 link_set_state(link
, LINK_STATE_CONFIGURING
);
333 int manager_rtnl_process_nexthop(sd_netlink
*rtnl
, sd_netlink_message
*message
, Manager
*m
) {
334 _cleanup_(nexthop_freep
) NextHop
*tmp
= NULL
;
335 _cleanup_free_
char *gateway
= NULL
;
336 NextHop
*nexthop
= NULL
;
346 if (sd_netlink_message_is_error(message
)) {
347 r
= sd_netlink_message_get_errno(message
);
349 log_message_warning_errno(message
, r
, "rtnl: failed to receive rule message, ignoring");
354 r
= sd_netlink_message_get_type(message
, &type
);
356 log_warning_errno(r
, "rtnl: could not get message type, ignoring: %m");
358 } else if (!IN_SET(type
, RTM_NEWNEXTHOP
, RTM_DELNEXTHOP
)) {
359 log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type
);
363 r
= sd_netlink_message_read_u32(message
, NHA_OIF
, &ifindex
);
365 log_warning_errno(r
, "rtnl: received nexthop message without NHA_OIF attribute, ignoring: %m");
368 log_warning_errno(r
, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
370 } else if (ifindex
<= 0) {
371 log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32
", ignoring.", ifindex
);
375 r
= link_get(m
, ifindex
, &link
);
376 if (r
< 0 || !link
) {
378 log_warning("rtnl: received nexthop message for link (%"PRIu32
") we do not know about, ignoring", ifindex
);
382 r
= nexthop_new(&tmp
);
386 r
= sd_rtnl_message_get_family(message
, &tmp
->family
);
388 log_link_warning_errno(link
, r
, "rtnl: could not get nexthop family, ignoring: %m");
390 } else if (!IN_SET(tmp
->family
, AF_INET
, AF_INET6
))
391 return log_link_debug(link
, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp
->family
);
393 r
= netlink_message_read_in_addr_union(message
, NHA_GATEWAY
, tmp
->family
, &tmp
->gw
);
394 if (r
< 0 && r
!= -ENODATA
) {
395 log_link_warning_errno(link
, r
, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
399 r
= sd_netlink_message_read_u32(message
, NHA_ID
, &tmp
->id
);
400 if (r
< 0 && r
!= -ENODATA
) {
401 log_link_warning_errno(link
, r
, "rtnl: could not get NHA_ID attribute, ignoring: %m");
405 (void) nexthop_get(link
, tmp
, &nexthop
);
408 (void) in_addr_to_string(tmp
->family
, &tmp
->gw
, &gateway
);
413 log_link_debug(link
, "Received remembered nexthop: %s, id: %d", strna(gateway
), tmp
->id
);
415 log_link_debug(link
, "Remembering foreign nexthop: %s, id: %d", strna(gateway
), tmp
->id
);
416 r
= nexthop_add_foreign(link
, tmp
, &nexthop
);
418 log_link_warning_errno(link
, r
, "Could not remember foreign nexthop, ignoring: %m");
425 log_link_debug(link
, "Forgetting nexthop: %s, id: %d", strna(gateway
), tmp
->id
);
426 nexthop_free(nexthop
);
428 log_link_debug(link
, "Kernel removed a nexthop we don't remember: %s, id: %d, ignoring.",
429 strna(gateway
), tmp
->id
);
433 assert_not_reached("Received invalid RTNL message type");
439 static int nexthop_section_verify(NextHop
*nh
) {
440 if (section_is_invalid(nh
->section
))
443 if (in_addr_is_null(nh
->family
, &nh
->gw
) < 0)
449 void network_drop_invalid_nexthops(Network
*network
) {
454 HASHMAP_FOREACH(nh
, network
->nexthops_by_section
)
455 if (nexthop_section_verify(nh
) < 0)
459 int config_parse_nexthop_id(
461 const char *filename
,
464 unsigned section_line
,
471 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
472 Network
*network
= userdata
;
481 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
485 r
= safe_atou32(rvalue
, &n
->id
);
487 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
488 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue
);
496 int config_parse_nexthop_gateway(
498 const char *filename
,
501 unsigned section_line
,
508 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
509 Network
*network
= userdata
;
518 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
522 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->gw
);
524 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
525 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);