1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/rtnetlink.h>
5 #include "alloc-util.h"
6 #include "networkd-address.h"
7 #include "networkd-link.h"
8 #include "networkd-manager.h"
9 #include "networkd-route-util.h"
10 #include "networkd-route.h"
11 #include "parse-util.h"
12 #include "string-table.h"
13 #include "string-util.h"
15 #include "sysctl-util.h"
17 #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
19 unsigned routes_max(void) {
20 static thread_local
unsigned cached
= 0;
21 _cleanup_free_
char *s4
= NULL
, *s6
= NULL
;
22 unsigned val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
27 if (sysctl_read_ip_property(AF_INET
, NULL
, "route/max_size", &s4
) >= 0)
28 if (safe_atou(s4
, &val4
) >= 0 && val4
== 2147483647U)
29 /* This is the default "no limit" value in the kernel */
30 val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
32 if (sysctl_read_ip_property(AF_INET6
, NULL
, "route/max_size", &s6
) >= 0)
33 (void) safe_atou(s6
, &val6
);
35 cached
= MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val4
) +
36 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
);
40 static Route
*link_find_default_gateway(Link
*link
, int family
, Route
*gw
) {
45 SET_FOREACH(route
, link
->routes
) {
46 if (!route_exists(route
))
48 if (family
!= AF_UNSPEC
&& route
->family
!= family
)
50 if (route
->dst_prefixlen
!= 0)
52 if (route
->src_prefixlen
!= 0)
54 if (route
->table
!= RT_TABLE_MAIN
)
56 if (route
->type
!= RTN_UNICAST
)
58 if (route
->scope
!= RT_SCOPE_UNIVERSE
)
60 if (!in_addr_is_set(route
->gw_family
, &route
->gw
))
63 if (route
->gw_weight
> gw
->gw_weight
)
65 if (route
->priority
>= gw
->priority
)
74 int manager_find_uplink(Manager
*m
, int family
, Link
*exclude
, Link
**ret
) {
79 assert(IN_SET(family
, AF_UNSPEC
, AF_INET
, AF_INET6
));
81 /* Looks for a suitable "uplink", via black magic: an interface that is up and where the
82 * default route with the highest priority points to. */
84 HASHMAP_FOREACH(link
, m
->links_by_index
) {
88 if (link
->state
!= LINK_STATE_CONFIGURED
)
91 gw
= link_find_default_gateway(link
, family
, gw
);
105 bool gateway_is_ready(Link
*link
, bool onlink
, int family
, const union in_addr_union
*gw
) {
110 assert(link
->manager
);
115 if (!gw
|| !in_addr_is_set(family
, gw
))
118 if (family
== AF_INET6
&& in6_addr_is_link_local(&gw
->in6
))
121 SET_FOREACH(route
, link
->routes
) {
122 if (!route_exists(route
))
124 if (route
->family
!= family
)
126 if (!in_addr_is_set(route
->family
, &route
->dst
) && route
->dst_prefixlen
== 0)
128 if (in_addr_prefix_covers(family
, &route
->dst
, route
->dst_prefixlen
, gw
) > 0)
132 if (link
->manager
->manage_foreign_routes
)
135 /* If we do not manage foreign routes, then there may exist a prefix route we do not know,
136 * which was created on configuring an address. Hence, also check the addresses. */
137 SET_FOREACH(a
, link
->addresses
) {
138 if (!address_is_ready(a
))
140 if (a
->family
!= family
)
142 if (FLAGS_SET(a
->flags
, IFA_F_NOPREFIXROUTE
))
144 if (in_addr_is_set(a
->family
, &a
->in_addr_peer
))
146 if (in_addr_prefix_covers(family
, &a
->in_addr
, a
->prefixlen
, gw
) > 0)
153 static int link_address_is_reachable_internal(
156 const union in_addr_union
*address
,
157 const union in_addr_union
*prefsrc
, /* optional */
160 Route
*route
, *found
= NULL
;
163 assert(IN_SET(family
, AF_INET
, AF_INET6
));
166 SET_FOREACH(route
, link
->routes
) {
167 if (!route_exists(route
))
170 if (route
->type
!= RTN_UNICAST
)
173 if (route
->family
!= family
)
176 if (in_addr_prefix_covers(family
, &route
->dst
, route
->dst_prefixlen
, address
) <= 0)
180 in_addr_is_set(family
, prefsrc
) &&
181 in_addr_is_set(family
, &route
->prefsrc
) &&
182 !in_addr_equal(family
, prefsrc
, &route
->prefsrc
))
185 if (found
&& found
->priority
<= route
->priority
)
200 int link_address_is_reachable(
203 const union in_addr_union
*address
,
204 const union in_addr_union
*prefsrc
, /* optional */
212 assert(IN_SET(family
, AF_INET
, AF_INET6
));
215 /* This checks if the address is reachable, and optionally return the Address object of the
216 * preferred source to access the address. */
218 r
= link_address_is_reachable_internal(link
, family
, address
, prefsrc
, &route
);
222 if (!in_addr_is_set(route
->family
, &route
->prefsrc
)) {
228 r
= link_get_address(link
, route
->family
, &route
->prefsrc
, 0, &a
);
232 if (!address_is_ready(a
))
241 int manager_address_is_reachable(
244 const union in_addr_union
*address
,
245 const union in_addr_union
*prefsrc
, /* optional */
248 Route
*route
, *found
= NULL
;
255 HASHMAP_FOREACH(link
, manager
->links_by_index
) {
256 if (!IN_SET(link
->state
, LINK_STATE_CONFIGURING
, LINK_STATE_CONFIGURED
))
259 if (link_address_is_reachable_internal(link
, family
, address
, prefsrc
, &route
) < 0)
262 if (found
&& found
->priority
<= route
->priority
)
271 if (!in_addr_is_set(found
->family
, &found
->prefsrc
)) {
277 r
= link_get_address(found
->link
, found
->family
, &found
->prefsrc
, 0, &a
);
281 if (!address_is_ready(a
))
290 static const char * const route_type_table
[__RTN_MAX
] = {
291 [RTN_UNICAST
] = "unicast",
292 [RTN_LOCAL
] = "local",
293 [RTN_BROADCAST
] = "broadcast",
294 [RTN_ANYCAST
] = "anycast",
295 [RTN_MULTICAST
] = "multicast",
296 [RTN_BLACKHOLE
] = "blackhole",
297 [RTN_UNREACHABLE
] = "unreachable",
298 [RTN_PROHIBIT
] = "prohibit",
299 [RTN_THROW
] = "throw",
301 [RTN_XRESOLVE
] = "xresolve",
304 assert_cc(__RTN_MAX
<= UCHAR_MAX
);
305 DEFINE_STRING_TABLE_LOOKUP(route_type
, int);
307 static const char * const route_scope_table
[] = {
308 [RT_SCOPE_UNIVERSE
] = "global",
309 [RT_SCOPE_SITE
] = "site",
310 [RT_SCOPE_LINK
] = "link",
311 [RT_SCOPE_HOST
] = "host",
312 [RT_SCOPE_NOWHERE
] = "nowhere",
315 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_scope
, int, UINT8_MAX
);
317 static const char * const route_protocol_table
[] = {
318 [RTPROT_KERNEL
] = "kernel",
319 [RTPROT_BOOT
] = "boot",
320 [RTPROT_STATIC
] = "static",
323 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol
, int, UINT8_MAX
);
325 static const char * const route_protocol_full_table
[] = {
326 [RTPROT_REDIRECT
] = "redirect",
327 [RTPROT_KERNEL
] = "kernel",
328 [RTPROT_BOOT
] = "boot",
329 [RTPROT_STATIC
] = "static",
330 [RTPROT_GATED
] = "gated",
332 [RTPROT_MRT
] = "mrt",
333 [RTPROT_ZEBRA
] = "zebra",
334 [RTPROT_BIRD
] = "bird",
335 [RTPROT_DNROUTED
] = "dnrouted",
336 [RTPROT_XORP
] = "xorp",
337 [RTPROT_NTK
] = "ntk",
338 [RTPROT_DHCP
] = "dhcp",
339 [RTPROT_MROUTED
] = "mrouted",
340 [RTPROT_BABEL
] = "babel",
341 [RTPROT_BGP
] = "bgp",
342 [RTPROT_ISIS
] = "isis",
343 [RTPROT_OSPF
] = "ospf",
344 [RTPROT_RIP
] = "rip",
345 [RTPROT_EIGRP
] = "eigrp",
348 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol_full
, int, UINT8_MAX
);
350 int route_flags_to_string_alloc(uint32_t flags
, char **ret
) {
351 _cleanup_free_
char *str
= NULL
;
352 static const char* map
[] = {
353 [LOG2U(RTNH_F_DEAD
)] = "dead", /* Nexthop is dead (used by multipath) */
354 [LOG2U(RTNH_F_PERVASIVE
)] = "pervasive", /* Do recursive gateway lookup */
355 [LOG2U(RTNH_F_ONLINK
)] = "onlink" , /* Gateway is forced on link */
356 [LOG2U(RTNH_F_OFFLOAD
)] = "offload", /* Nexthop is offloaded */
357 [LOG2U(RTNH_F_LINKDOWN
)] = "linkdown", /* carrier-down on nexthop */
358 [LOG2U(RTNH_F_UNRESOLVED
)] = "unresolved", /* The entry is unresolved (ipmr) */
359 [LOG2U(RTNH_F_TRAP
)] = "trap", /* Nexthop is trapping packets */
364 for (size_t i
= 0; i
< ELEMENTSOF(map
); i
++)
365 if (FLAGS_SET(flags
, 1 << i
) && map
[i
])
366 if (!strextend_with_separator(&str
, ",", map
[i
]))
369 *ret
= TAKE_PTR(str
);
373 static const char * const route_table_table
[] = {
374 [RT_TABLE_DEFAULT
] = "default",
375 [RT_TABLE_MAIN
] = "main",
376 [RT_TABLE_LOCAL
] = "local",
379 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table
, int);
381 int manager_get_route_table_from_string(const Manager
*m
, const char *s
, uint32_t *ret
) {
389 r
= route_table_from_string(s
);
395 t
= PTR_TO_UINT32(hashmap_get(m
->route_table_numbers_by_name
, s
));
401 r
= safe_atou32(s
, &t
);
412 int manager_get_route_table_to_string(const Manager
*m
, uint32_t table
, char **ret
) {
413 _cleanup_free_
char *str
= NULL
;
423 s
= route_table_to_string(table
);
425 s
= hashmap_get(m
->route_table_names_by_number
, UINT32_TO_PTR(table
));
428 /* Currently, this is only used in debugging logs. To not confuse any bug
429 * reports, let's include the table number. */
430 r
= asprintf(&str
, "%s(%" PRIu32
")", s
, table
);
432 r
= asprintf(&str
, "%" PRIu32
, table
);
436 *ret
= TAKE_PTR(str
);
440 int config_parse_route_table_names(
442 const char *filename
,
445 unsigned section_line
,
452 Manager
*m
= ASSERT_PTR(userdata
);
459 if (isempty(rvalue
)) {
460 m
->route_table_names_by_number
= hashmap_free(m
->route_table_names_by_number
);
461 m
->route_table_numbers_by_name
= hashmap_free(m
->route_table_numbers_by_name
);
465 for (const char *p
= rvalue
;;) {
466 _cleanup_free_
char *name
= NULL
;
470 r
= extract_first_word(&p
, &name
, NULL
, 0);
474 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
475 "Invalid RouteTable=, ignoring assignment: %s", rvalue
);
481 num
= strchr(name
, ':');
483 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
484 "Invalid route table name and number pair, ignoring assignment: %s", name
);
491 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
492 "Route table name cannot be empty. Ignoring assignment: %s:%s", name
, num
);
495 if (in_charset(name
, DIGITS
)) {
496 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
497 "Route table name cannot be numeric. Ignoring assignment: %s:%s", name
, num
);
500 if (STR_IN_SET(name
, "default", "main", "local")) {
501 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
502 "Route table name %s is already predefined. Ignoring assignment: %s:%s", name
, name
, num
);
506 r
= safe_atou32(num
, &table
);
508 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
509 "Failed to parse route table number '%s', ignoring assignment: %s:%s", num
, name
, num
);
513 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
514 "Invalid route table number, ignoring assignment: %s:%s", name
, num
);
518 r
= hashmap_ensure_put(&m
->route_table_numbers_by_name
, &string_hash_ops_free
, name
, UINT32_TO_PTR(table
));
522 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
523 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name
, num
);
527 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
528 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name
, num
);
532 /* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
535 r
= hashmap_ensure_put(&m
->route_table_names_by_number
, NULL
, UINT32_TO_PTR(table
), name
);
537 hashmap_remove(m
->route_table_numbers_by_name
, name
);
542 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
543 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name
, num
);
545 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
546 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name
, num
);