1 /* SPDX-License-Identifier: LGPL-2.1+
2 * Copyright © 2019 VMware, Inc.
5 #include <linux/nexthop.h>
7 #include "alloc-util.h"
8 #include "conf-parser.h"
9 #include "in-addr-util.h"
10 #include "netlink-util.h"
11 #include "networkd-manager.h"
12 #include "networkd-nexthop.h"
13 #include "parse-util.h"
15 #include "string-util.h"
18 int nexthop_new(NextHop
**ret
) {
19 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
21 nexthop
= new(NextHop
, 1);
25 *nexthop
= (NextHop
) {
29 *ret
= TAKE_PTR(nexthop
);
34 static int nexthop_new_static(Network
*network
, const char *filename
, unsigned section_line
, NextHop
**ret
) {
35 _cleanup_(network_config_section_freep
) NetworkConfigSection
*n
= NULL
;
36 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
41 assert(!!filename
== (section_line
> 0));
44 r
= network_config_section_new(filename
, section_line
, &n
);
48 nexthop
= hashmap_get(network
->nexthops_by_section
, n
);
50 *ret
= TAKE_PTR(nexthop
);
56 r
= nexthop_new(&nexthop
);
60 nexthop
->protocol
= RTPROT_STATIC
;
61 nexthop
->network
= network
;
62 LIST_PREPEND(nexthops
, network
->static_nexthops
, nexthop
);
63 network
->n_static_nexthops
++;
66 nexthop
->section
= TAKE_PTR(n
);
68 r
= hashmap_ensure_allocated(&network
->nexthops_by_section
, &network_config_hash_ops
);
72 r
= hashmap_put(network
->nexthops_by_section
, nexthop
->section
, nexthop
);
77 *ret
= TAKE_PTR(nexthop
);
82 void nexthop_free(NextHop
*nexthop
) {
86 if (nexthop
->network
) {
87 LIST_REMOVE(nexthops
, nexthop
->network
->static_nexthops
, nexthop
);
89 assert(nexthop
->network
->n_static_nexthops
> 0);
90 nexthop
->network
->n_static_nexthops
--;
93 hashmap_remove(nexthop
->network
->nexthops_by_section
, nexthop
->section
);
96 network_config_section_free(nexthop
->section
);
99 set_remove(nexthop
->link
->nexthops
, nexthop
);
100 set_remove(nexthop
->link
->nexthops_foreign
, nexthop
);
106 static void nexthop_hash_func(const NextHop
*nexthop
, struct siphash
*state
) {
109 siphash24_compress(&nexthop
->id
, sizeof(nexthop
->id
), state
);
110 siphash24_compress(&nexthop
->oif
, sizeof(nexthop
->oif
), state
);
111 siphash24_compress(&nexthop
->family
, sizeof(nexthop
->family
), state
);
113 switch (nexthop
->family
) {
116 siphash24_compress(&nexthop
->gw
, FAMILY_ADDRESS_SIZE(nexthop
->family
), state
);
120 /* treat any other address family as AF_UNSPEC */
125 static int nexthop_compare_func(const NextHop
*a
, const NextHop
*b
) {
128 r
= CMP(a
->id
, b
->id
);
132 r
= CMP(a
->oif
, b
->oif
);
136 r
= CMP(a
->family
, b
->family
);
144 r
= memcmp(&a
->gw
, &b
->gw
, FAMILY_ADDRESS_SIZE(a
->family
));
150 /* treat any other address family as AF_UNSPEC */
155 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
159 nexthop_compare_func
,
162 bool nexthop_equal(NextHop
*r1
, NextHop
*r2
) {
169 return nexthop_compare_func(r1
, r2
) == 0;
172 int nexthop_get(Link
*link
, NextHop
*in
, NextHop
**ret
) {
178 existing
= set_get(link
->nexthops
, in
);
185 existing
= set_get(link
->nexthops_foreign
, in
);
195 static int nexthop_add_internal(Link
*link
, Set
**nexthops
, NextHop
*in
, NextHop
**ret
) {
196 _cleanup_(nexthop_freep
) NextHop
*nexthop
= NULL
;
203 r
= nexthop_new(&nexthop
);
207 nexthop
->id
= in
->id
;
208 nexthop
->oif
= in
->oif
;
209 nexthop
->family
= in
->family
;
210 nexthop
->gw
= in
->gw
;
212 r
= set_ensure_allocated(nexthops
, &nexthop_hash_ops
);
216 r
= set_put(*nexthops
, nexthop
);
222 nexthop
->link
= link
;
232 int nexthop_add_foreign(Link
*link
, NextHop
*in
, NextHop
**ret
) {
233 return nexthop_add_internal(link
, &link
->nexthops_foreign
, in
, ret
);
236 int nexthop_add(Link
*link
, NextHop
*in
, NextHop
**ret
) {
240 r
= nexthop_get(link
, in
, &nexthop
);
242 /* NextHop does not exist, create a new one */
243 r
= nexthop_add_internal(link
, &link
->nexthops
, in
, &nexthop
);
247 /* Take over a foreign nexthop */
248 r
= set_ensure_allocated(&link
->nexthops
, &nexthop_hash_ops
);
252 r
= set_put(link
->nexthops
, nexthop
);
256 set_remove(link
->nexthops_foreign
, nexthop
);
258 /* NextHop exists, do nothing */
269 static int nexthop_remove_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
274 assert(link
->ifname
);
276 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
279 r
= sd_netlink_message_get_errno(m
);
280 if (r
< 0 && r
!= -ESRCH
)
281 log_link_message_warning_errno(link
, m
, r
, "Could not drop nexthop, ignoring");
286 int nexthop_remove(NextHop
*nexthop
, Link
*link
,
287 link_netlink_message_handler_t callback
) {
289 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
293 assert(link
->manager
);
294 assert(link
->manager
->rtnl
);
295 assert(link
->ifindex
> 0);
296 assert(IN_SET(nexthop
->family
, AF_INET
, AF_INET6
));
298 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &req
,
299 RTM_DELNEXTHOP
, nexthop
->family
,
302 return log_link_error_errno(link
, r
, "Could not create RTM_DELNEXTHOP message: %m");
305 _cleanup_free_
char *gw
= NULL
;
307 if (!in_addr_is_null(nexthop
->family
, &nexthop
->gw
))
308 (void) in_addr_to_string(nexthop
->family
, &nexthop
->gw
, &gw
);
310 log_link_debug(link
, "Removing nexthop: gw: %s", strna(gw
));
313 if (in_addr_is_null(nexthop
->family
, &nexthop
->gw
) == 0) {
314 r
= netlink_message_append_in_addr_union(req
, RTA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
316 return log_link_error_errno(link
, r
, "Could not append RTA_GATEWAY attribute: %m");
319 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
,
320 callback
?: nexthop_remove_handler
,
321 link_netlink_destroy_callback
, link
);
323 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
330 int nexthop_configure(
333 link_netlink_message_handler_t callback
) {
334 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
338 assert(link
->manager
);
339 assert(link
->manager
->rtnl
);
340 assert(link
->ifindex
> 0);
341 assert(IN_SET(nexthop
->family
, AF_INET
, AF_INET6
));
345 _cleanup_free_
char *gw
= NULL
;
347 if (!in_addr_is_null(nexthop
->family
, &nexthop
->gw
))
348 (void) in_addr_to_string(nexthop
->family
, &nexthop
->gw
, &gw
);
350 log_link_debug(link
, "Configuring nexthop: gw: %s", strna(gw
));
353 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &req
,
354 RTM_NEWNEXTHOP
, nexthop
->family
,
357 return log_link_error_errno(link
, r
, "Could not create RTM_NEWNEXTHOP message: %m");
359 r
= sd_netlink_message_append_u32(req
, NHA_ID
, nexthop
->id
);
361 return log_link_error_errno(link
, r
, "Could not append NHA_ID attribute: %m");
363 r
= sd_netlink_message_append_u32(req
, NHA_OIF
, link
->ifindex
);
365 return log_link_error_errno(link
, r
, "Could not append NHA_OIF attribute: %m");
367 if (in_addr_is_null(nexthop
->family
, &nexthop
->gw
) == 0) {
368 r
= netlink_message_append_in_addr_union(req
, NHA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
370 return log_link_error_errno(link
, r
, "Could not append NHA_GATEWAY attribute: %m");
372 r
= sd_rtnl_message_nexthop_set_family(req
, nexthop
->family
);
374 return log_link_error_errno(link
, r
, "Could not set nexthop family: %m");
377 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, callback
,
378 link_netlink_destroy_callback
, link
);
380 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
384 r
= nexthop_add(link
, nexthop
, &nexthop
);
386 return log_link_error_errno(link
, r
, "Could not add nexthop: %m");
391 int nexthop_section_verify(NextHop
*nh
) {
392 if (section_is_invalid(nh
->section
))
395 if (in_addr_is_null(nh
->family
, &nh
->gw
) < 0)
401 int config_parse_nexthop_id(
403 const char *filename
,
406 unsigned section_line
,
413 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
414 Network
*network
= userdata
;
423 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
427 r
= safe_atou32(rvalue
, &n
->id
);
429 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
430 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue
);
438 int config_parse_nexthop_gateway(
440 const char *filename
,
443 unsigned section_line
,
450 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
451 Network
*network
= userdata
;
460 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
464 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->gw
);
466 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
467 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);