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_put(nexthops
, &nexthop_hash_ops
, nexthop
);
218 nexthop
->link
= link
;
228 int nexthop_add_foreign(Link
*link
, NextHop
*in
, NextHop
**ret
) {
229 return nexthop_add_internal(link
, &link
->nexthops_foreign
, in
, ret
);
232 int nexthop_add(Link
*link
, NextHop
*in
, NextHop
**ret
) {
236 r
= nexthop_get(link
, in
, &nexthop
);
238 /* NextHop does not exist, create a new one */
239 r
= nexthop_add_internal(link
, &link
->nexthops
, in
, &nexthop
);
243 /* Take over a foreign nexthop */
244 r
= set_ensure_put(&link
->nexthops
, &nexthop_hash_ops
, nexthop
);
248 set_remove(link
->nexthops_foreign
, nexthop
);
250 /* NextHop exists, do nothing */
261 static int nexthop_remove_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, Link
*link
) {
266 assert(link
->ifname
);
268 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
271 r
= sd_netlink_message_get_errno(m
);
272 if (r
< 0 && r
!= -ESRCH
)
273 log_link_message_warning_errno(link
, m
, r
, "Could not drop nexthop, ignoring");
278 int nexthop_remove(NextHop
*nexthop
, Link
*link
,
279 link_netlink_message_handler_t callback
) {
281 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
285 assert(link
->manager
);
286 assert(link
->manager
->rtnl
);
287 assert(link
->ifindex
> 0);
288 assert(IN_SET(nexthop
->family
, AF_INET
, AF_INET6
));
290 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &req
,
291 RTM_DELNEXTHOP
, nexthop
->family
,
294 return log_link_error_errno(link
, r
, "Could not create RTM_DELNEXTHOP message: %m");
297 _cleanup_free_
char *gw
= NULL
;
299 if (!in_addr_is_null(nexthop
->family
, &nexthop
->gw
))
300 (void) in_addr_to_string(nexthop
->family
, &nexthop
->gw
, &gw
);
302 log_link_debug(link
, "Removing nexthop: gw: %s", strna(gw
));
305 if (in_addr_is_null(nexthop
->family
, &nexthop
->gw
) == 0) {
306 r
= netlink_message_append_in_addr_union(req
, RTA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
308 return log_link_error_errno(link
, r
, "Could not append RTA_GATEWAY attribute: %m");
311 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
,
312 callback
?: nexthop_remove_handler
,
313 link_netlink_destroy_callback
, link
);
315 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
322 int nexthop_configure(
325 link_netlink_message_handler_t callback
) {
326 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
330 assert(link
->manager
);
331 assert(link
->manager
->rtnl
);
332 assert(link
->ifindex
> 0);
333 assert(IN_SET(nexthop
->family
, AF_INET
, AF_INET6
));
337 _cleanup_free_
char *gw
= NULL
;
339 if (!in_addr_is_null(nexthop
->family
, &nexthop
->gw
))
340 (void) in_addr_to_string(nexthop
->family
, &nexthop
->gw
, &gw
);
342 log_link_debug(link
, "Configuring nexthop: gw: %s", strna(gw
));
345 r
= sd_rtnl_message_new_nexthop(link
->manager
->rtnl
, &req
,
346 RTM_NEWNEXTHOP
, nexthop
->family
,
349 return log_link_error_errno(link
, r
, "Could not create RTM_NEWNEXTHOP message: %m");
351 r
= sd_netlink_message_append_u32(req
, NHA_ID
, nexthop
->id
);
353 return log_link_error_errno(link
, r
, "Could not append NHA_ID attribute: %m");
355 r
= sd_netlink_message_append_u32(req
, NHA_OIF
, link
->ifindex
);
357 return log_link_error_errno(link
, r
, "Could not append NHA_OIF attribute: %m");
359 if (in_addr_is_null(nexthop
->family
, &nexthop
->gw
) == 0) {
360 r
= netlink_message_append_in_addr_union(req
, NHA_GATEWAY
, nexthop
->family
, &nexthop
->gw
);
362 return log_link_error_errno(link
, r
, "Could not append NHA_GATEWAY attribute: %m");
364 r
= sd_rtnl_message_nexthop_set_family(req
, nexthop
->family
);
366 return log_link_error_errno(link
, r
, "Could not set nexthop family: %m");
369 r
= netlink_call_async(link
->manager
->rtnl
, NULL
, req
, callback
,
370 link_netlink_destroy_callback
, link
);
372 return log_link_error_errno(link
, r
, "Could not send rtnetlink message: %m");
376 r
= nexthop_add(link
, nexthop
, &nexthop
);
378 return log_link_error_errno(link
, r
, "Could not add nexthop: %m");
383 int nexthop_section_verify(NextHop
*nh
) {
384 if (section_is_invalid(nh
->section
))
387 if (in_addr_is_null(nh
->family
, &nh
->gw
) < 0)
393 int config_parse_nexthop_id(
395 const char *filename
,
398 unsigned section_line
,
405 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
406 Network
*network
= userdata
;
415 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
419 r
= safe_atou32(rvalue
, &n
->id
);
421 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
422 "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue
);
430 int config_parse_nexthop_gateway(
432 const char *filename
,
435 unsigned section_line
,
442 _cleanup_(nexthop_free_or_set_invalidp
) NextHop
*n
= NULL
;
443 Network
*network
= userdata
;
452 r
= nexthop_new_static(network
, filename
, section_line
, &n
);
456 r
= in_addr_from_string_auto(rvalue
, &n
->family
, &n
->gw
);
458 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
459 "Invalid %s='%s', ignoring assignment: %m", lvalue
, rvalue
);