1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/rtnetlink.h>
5 #include "alloc-util.h"
7 #include "missing_threads.h"
8 #include "networkd-address.h"
9 #include "networkd-link.h"
10 #include "networkd-manager.h"
11 #include "networkd-route-util.h"
12 #include "networkd-route.h"
13 #include "parse-util.h"
14 #include "string-table.h"
15 #include "string-util.h"
17 #include "sysctl-util.h"
19 #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
21 unsigned routes_max(void) {
22 static thread_local
unsigned cached
= 0;
23 _cleanup_free_
char *s4
= NULL
, *s6
= NULL
;
24 unsigned val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
29 if (sysctl_read_ip_property(AF_INET
, NULL
, "route/max_size", &s4
) >= 0)
30 if (safe_atou(s4
, &val4
) >= 0 && val4
== 2147483647U)
31 /* This is the default "no limit" value in the kernel */
32 val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
34 if (sysctl_read_ip_property(AF_INET6
, NULL
, "route/max_size", &s6
) >= 0)
35 (void) safe_atou(s6
, &val6
);
37 cached
= MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val4
) +
38 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
);
42 static bool route_lifetime_is_valid(const Route
*route
) {
46 route
->lifetime_usec
== USEC_INFINITY
||
47 route
->lifetime_usec
> now(CLOCK_BOOTTIME
);
50 bool link_find_default_gateway(Link
*link
, int family
, Route
**gw
) {
56 SET_FOREACH(route
, link
->routes
) {
57 if (!route_exists(route
))
59 if (family
!= AF_UNSPEC
&& route
->family
!= family
)
61 if (route
->dst_prefixlen
!= 0)
63 if (route
->src_prefixlen
!= 0)
65 if (route
->table
!= RT_TABLE_MAIN
)
67 if (route
->type
!= RTN_UNICAST
)
69 if (route
->scope
!= RT_SCOPE_UNIVERSE
)
71 if (!in_addr_is_set(route
->gw_family
, &route
->gw
))
74 /* Found a default gateway. */
78 /* If we have already found another gw, then let's compare their weight and priority. */
80 if (route
->gw_weight
> (*gw
)->gw_weight
)
82 if (route
->priority
>= (*gw
)->priority
)
93 int manager_find_uplink(Manager
*m
, int family
, Link
*exclude
, Link
**ret
) {
98 assert(IN_SET(family
, AF_UNSPEC
, AF_INET
, AF_INET6
));
100 /* Looks for a suitable "uplink", via black magic: an interface that is up and where the
101 * default route with the highest priority points to. */
103 HASHMAP_FOREACH(link
, m
->links_by_index
) {
107 if (link
->state
!= LINK_STATE_CONFIGURED
)
110 link_find_default_gateway(link
, family
, &gw
);
124 bool gateway_is_ready(Link
*link
, bool onlink
, int family
, const union in_addr_union
*gw
) {
129 assert(link
->manager
);
134 if (!gw
|| !in_addr_is_set(family
, gw
))
137 if (family
== AF_INET6
&& in6_addr_is_link_local(&gw
->in6
))
140 SET_FOREACH(route
, link
->routes
) {
141 if (!route_exists(route
))
143 if (!route_lifetime_is_valid(route
))
145 if (route
->family
!= family
)
147 if (!in_addr_is_set(route
->family
, &route
->dst
) && route
->dst_prefixlen
== 0)
149 if (in_addr_prefix_covers(family
, &route
->dst
, route
->dst_prefixlen
, gw
) > 0)
153 if (link
->manager
->manage_foreign_routes
)
156 /* If we do not manage foreign routes, then there may exist a prefix route we do not know,
157 * which was created on configuring an address. Hence, also check the addresses. */
158 SET_FOREACH(a
, link
->addresses
) {
159 if (!address_is_ready(a
))
161 if (a
->family
!= family
)
163 if (FLAGS_SET(a
->flags
, IFA_F_NOPREFIXROUTE
))
165 if (in_addr_prefix_covers(a
->family
,
166 in_addr_is_set(a
->family
, &a
->in_addr_peer
) ? &a
->in_addr_peer
: &a
->in_addr
,
167 a
->prefixlen
, gw
) > 0)
174 static int link_address_is_reachable_internal(
177 const union in_addr_union
*address
,
178 const union in_addr_union
*prefsrc
, /* optional */
181 Route
*route
, *found
= NULL
;
184 assert(IN_SET(family
, AF_INET
, AF_INET6
));
187 SET_FOREACH(route
, link
->routes
) {
188 if (!route_exists(route
))
191 if (!route_lifetime_is_valid(route
))
194 if (route
->type
!= RTN_UNICAST
)
197 if (route
->family
!= family
)
200 if (in_addr_prefix_covers(family
, &route
->dst
, route
->dst_prefixlen
, address
) <= 0)
204 in_addr_is_set(family
, prefsrc
) &&
205 in_addr_is_set(family
, &route
->prefsrc
) &&
206 !in_addr_equal(family
, prefsrc
, &route
->prefsrc
))
209 if (found
&& found
->priority
<= route
->priority
)
224 int link_address_is_reachable(
227 const union in_addr_union
*address
,
228 const union in_addr_union
*prefsrc
, /* optional */
236 assert(IN_SET(family
, AF_INET
, AF_INET6
));
239 /* This checks if the address is reachable, and optionally return the Address object of the
240 * preferred source to access the address. */
242 r
= link_address_is_reachable_internal(link
, family
, address
, prefsrc
, &route
);
246 if (!in_addr_is_set(route
->family
, &route
->prefsrc
)) {
252 r
= link_get_address(link
, route
->family
, &route
->prefsrc
, 0, &a
);
256 if (!address_is_ready(a
))
265 int manager_address_is_reachable(
268 const union in_addr_union
*address
,
269 const union in_addr_union
*prefsrc
, /* optional */
272 Route
*route
, *found
= NULL
;
279 HASHMAP_FOREACH(link
, manager
->links_by_index
) {
280 if (!IN_SET(link
->state
, LINK_STATE_CONFIGURING
, LINK_STATE_CONFIGURED
))
283 if (link_address_is_reachable_internal(link
, family
, address
, prefsrc
, &route
) < 0)
286 if (found
&& found
->priority
<= route
->priority
)
295 if (!in_addr_is_set(found
->family
, &found
->prefsrc
)) {
301 r
= link_get_address(found
->link
, found
->family
, &found
->prefsrc
, 0, &a
);
305 if (!address_is_ready(a
))
314 static const char * const route_type_table
[__RTN_MAX
] = {
315 [RTN_UNICAST
] = "unicast",
316 [RTN_LOCAL
] = "local",
317 [RTN_BROADCAST
] = "broadcast",
318 [RTN_ANYCAST
] = "anycast",
319 [RTN_MULTICAST
] = "multicast",
320 [RTN_BLACKHOLE
] = "blackhole",
321 [RTN_UNREACHABLE
] = "unreachable",
322 [RTN_PROHIBIT
] = "prohibit",
323 [RTN_THROW
] = "throw",
325 [RTN_XRESOLVE
] = "xresolve",
328 assert_cc(__RTN_MAX
<= UCHAR_MAX
);
329 DEFINE_STRING_TABLE_LOOKUP(route_type
, int);
331 static const char * const route_scope_table
[] = {
332 [RT_SCOPE_UNIVERSE
] = "global",
333 [RT_SCOPE_SITE
] = "site",
334 [RT_SCOPE_LINK
] = "link",
335 [RT_SCOPE_HOST
] = "host",
336 [RT_SCOPE_NOWHERE
] = "nowhere",
339 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_scope
, int, UINT8_MAX
);
341 static const char * const route_protocol_table
[] = {
342 [RTPROT_KERNEL
] = "kernel",
343 [RTPROT_BOOT
] = "boot",
344 [RTPROT_STATIC
] = "static",
347 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol
, int, UINT8_MAX
);
349 static const char * const route_protocol_full_table
[] = {
350 [RTPROT_REDIRECT
] = "redirect",
351 [RTPROT_KERNEL
] = "kernel",
352 [RTPROT_BOOT
] = "boot",
353 [RTPROT_STATIC
] = "static",
354 [RTPROT_GATED
] = "gated",
356 [RTPROT_MRT
] = "mrt",
357 [RTPROT_ZEBRA
] = "zebra",
358 [RTPROT_BIRD
] = "bird",
359 [RTPROT_DNROUTED
] = "dnrouted",
360 [RTPROT_XORP
] = "xorp",
361 [RTPROT_NTK
] = "ntk",
362 [RTPROT_DHCP
] = "dhcp",
363 [RTPROT_MROUTED
] = "mrouted",
364 [RTPROT_BABEL
] = "babel",
365 [RTPROT_BGP
] = "bgp",
366 [RTPROT_ISIS
] = "isis",
367 [RTPROT_OSPF
] = "ospf",
368 [RTPROT_RIP
] = "rip",
369 [RTPROT_EIGRP
] = "eigrp",
372 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol_full
, int, UINT8_MAX
);
374 int route_flags_to_string_alloc(uint32_t flags
, char **ret
) {
375 _cleanup_free_
char *str
= NULL
;
376 static const char* map
[] = {
377 [LOG2U(RTNH_F_DEAD
)] = "dead", /* Nexthop is dead (used by multipath) */
378 [LOG2U(RTNH_F_PERVASIVE
)] = "pervasive", /* Do recursive gateway lookup */
379 [LOG2U(RTNH_F_ONLINK
)] = "onlink" , /* Gateway is forced on link */
380 [LOG2U(RTNH_F_OFFLOAD
)] = "offload", /* Nexthop is offloaded */
381 [LOG2U(RTNH_F_LINKDOWN
)] = "linkdown", /* carrier-down on nexthop */
382 [LOG2U(RTNH_F_UNRESOLVED
)] = "unresolved", /* The entry is unresolved (ipmr) */
383 [LOG2U(RTNH_F_TRAP
)] = "trap", /* Nexthop is trapping packets */
388 for (size_t i
= 0; i
< ELEMENTSOF(map
); i
++)
389 if (FLAGS_SET(flags
, 1 << i
) && map
[i
])
390 if (!strextend_with_separator(&str
, ",", map
[i
]))
393 *ret
= TAKE_PTR(str
);
397 static const char * const route_table_table
[] = {
398 [RT_TABLE_DEFAULT
] = "default",
399 [RT_TABLE_MAIN
] = "main",
400 [RT_TABLE_LOCAL
] = "local",
403 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table
, int);
405 int manager_get_route_table_from_string(const Manager
*m
, const char *s
, uint32_t *ret
) {
413 r
= route_table_from_string(s
);
419 t
= PTR_TO_UINT32(hashmap_get(m
->route_table_numbers_by_name
, s
));
425 r
= safe_atou32(s
, &t
);
436 int manager_get_route_table_to_string(const Manager
*m
, uint32_t table
, bool append_num
, char **ret
) {
437 _cleanup_free_
char *str
= NULL
;
443 /* Unlike manager_get_route_table_from_string(), this accepts 0, as the kernel may create routes with
444 * table 0. See issue #25089. */
446 s
= route_table_to_string(table
);
448 s
= hashmap_get(m
->route_table_names_by_number
, UINT32_TO_PTR(table
));
450 if (s
&& !append_num
) {
455 } else if (asprintf(&str
, "%s%s%" PRIu32
"%s",
462 *ret
= TAKE_PTR(str
);
466 int config_parse_route_table_names(
468 const char *filename
,
471 unsigned section_line
,
478 Manager
*m
= ASSERT_PTR(userdata
);
485 if (isempty(rvalue
)) {
486 m
->route_table_names_by_number
= hashmap_free(m
->route_table_names_by_number
);
487 m
->route_table_numbers_by_name
= hashmap_free(m
->route_table_numbers_by_name
);
491 for (const char *p
= rvalue
;;) {
492 _cleanup_free_
char *name
= NULL
;
496 r
= extract_first_word(&p
, &name
, NULL
, 0);
500 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
501 "Invalid RouteTable=, ignoring assignment: %s", rvalue
);
507 num
= strchr(name
, ':');
509 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
510 "Invalid route table name and number pair, ignoring assignment: %s", name
);
517 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
518 "Route table name cannot be empty. Ignoring assignment: %s:%s", name
, num
);
521 if (in_charset(name
, DIGITS
)) {
522 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
523 "Route table name cannot be numeric. Ignoring assignment: %s:%s", name
, num
);
526 if (route_table_from_string(name
) >= 0) {
527 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
528 "Route table name %s is predefined for %i. Ignoring assignment: %s:%s",
529 name
, route_table_from_string(name
), name
, num
);
533 r
= safe_atou32(num
, &table
);
535 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
536 "Failed to parse route table number '%s', ignoring assignment: %s:%s", num
, name
, num
);
540 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
541 "Invalid route table number, ignoring assignment: %s:%s", name
, num
);
544 if (route_table_to_string(table
)) {
545 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
546 "Route table name for %s is predefined (%s). Ignoring assignment: %s:%s",
547 num
, route_table_to_string(table
), name
, num
);
551 r
= hashmap_ensure_put(&m
->route_table_numbers_by_name
, &string_hash_ops_free
, name
, UINT32_TO_PTR(table
));
555 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
556 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name
, num
);
560 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
561 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name
, num
);
565 /* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
568 r
= hashmap_ensure_put(&m
->route_table_names_by_number
, NULL
, UINT32_TO_PTR(table
), name
);
570 hashmap_remove(m
->route_table_numbers_by_name
, name
);
575 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
576 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name
, num
);
578 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
579 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name
, num
);