/src/systemd/src/network/networkd-route.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include <linux/ipv6_route.h> |
4 | | #include <net/if.h> |
5 | | #include <stdio.h> |
6 | | |
7 | | #include "sd-ndisc-protocol.h" |
8 | | #include "sd-netlink.h" |
9 | | |
10 | | #include "alloc-util.h" |
11 | | #include "conf-parser.h" |
12 | | #include "errno-util.h" |
13 | | #include "event-util.h" |
14 | | #include "netlink-util.h" |
15 | | #include "networkd-address.h" |
16 | | #include "networkd-ipv4ll.h" |
17 | | #include "networkd-link.h" |
18 | | #include "networkd-manager.h" |
19 | | #include "networkd-network.h" |
20 | | #include "networkd-nexthop.h" |
21 | | #include "networkd-queue.h" |
22 | | #include "networkd-route.h" |
23 | | #include "networkd-route-util.h" |
24 | | #include "ordered-set.h" |
25 | | #include "parse-util.h" |
26 | | #include "set.h" |
27 | | #include "siphash24.h" |
28 | | #include "string-util.h" |
29 | | #include "vrf.h" |
30 | | #include "wireguard.h" |
31 | | |
32 | 95.0k | static Route* route_detach_impl(Route *route) { |
33 | 95.0k | assert(route); |
34 | 95.0k | assert(!!route->network + !!route->manager + !!route->wireguard <= 1); |
35 | | |
36 | 95.0k | if (route->network) { |
37 | 35.6k | assert(route->section); |
38 | 35.6k | hashmap_remove(route->network->routes_by_section, route->section); |
39 | 35.6k | route->network = NULL; |
40 | 35.6k | return route; |
41 | 35.6k | } |
42 | | |
43 | 59.3k | if (route->manager) { |
44 | 0 | route_detach_from_nexthop(route); |
45 | 0 | set_remove(route->manager->routes, route); |
46 | 0 | route->manager = NULL; |
47 | 0 | return route; |
48 | 0 | } |
49 | | |
50 | 59.3k | if (route->wireguard) { |
51 | 6.26k | set_remove(route->wireguard->routes, route); |
52 | 6.26k | route->wireguard = NULL; |
53 | 6.26k | return route; |
54 | 6.26k | } |
55 | | |
56 | 53.1k | return NULL; |
57 | 59.3k | } |
58 | | |
59 | 41.9k | void route_detach(Route *route) { |
60 | 41.9k | route_unref(route_detach_impl(route)); |
61 | 41.9k | } |
62 | | |
63 | 53.1k | static Route* route_free(Route *route) { |
64 | 53.1k | if (!route) |
65 | 0 | return NULL; |
66 | | |
67 | 53.1k | route_detach_impl(route); |
68 | | |
69 | 53.1k | config_section_free(route->section); |
70 | 53.1k | route_nexthops_done(route); |
71 | 53.1k | route_metric_done(&route->metric); |
72 | 53.1k | sd_event_source_disable_unref(route->expire); |
73 | | |
74 | 53.1k | return mfree(route); |
75 | 53.1k | } |
76 | | |
77 | 265k | DEFINE_TRIVIAL_REF_UNREF_FUNC(Route, route, route_free); Unexecuted instantiation: route_ref Unexecuted instantiation: route_unref |
78 | 265k | |
79 | 265k | static void route_hash_func(const Route *route, struct siphash *state) { |
80 | 32.1k | assert(route); |
81 | | |
82 | 32.1k | siphash24_compress_typesafe(route->family, state); |
83 | | |
84 | 32.1k | switch (route->family) { |
85 | 19.8k | case AF_INET: |
86 | | /* First, the table, destination prefix, priority, and tos (dscp), are used to find routes. |
87 | | * See fib_table_insert(), fib_find_node(), and fib_find_alias() in net/ipv4/fib_trie.c of the kernel. */ |
88 | 19.8k | siphash24_compress_typesafe(route->table, state); |
89 | 19.8k | in_addr_hash_func(&route->dst, route->family, state); |
90 | 19.8k | siphash24_compress_typesafe(route->dst_prefixlen, state); |
91 | 19.8k | siphash24_compress_typesafe(route->priority, state); |
92 | 19.8k | siphash24_compress_typesafe(route->tos, state); |
93 | | |
94 | | /* Then, protocol, scope, type, flags, prefsrc, metrics (RTAX_* attributes), and nexthops (gateways) |
95 | | * are used to find routes. See fib_find_info() in net/ipv4/fib_semantics.c of the kernel. */ |
96 | 19.8k | siphash24_compress_typesafe(route->protocol, state); |
97 | 19.8k | siphash24_compress_typesafe(route->scope, state); |
98 | 19.8k | siphash24_compress_typesafe(route->type, state); |
99 | 19.8k | unsigned flags = route->flags & ~RTNH_COMPARE_MASK; |
100 | 19.8k | siphash24_compress_typesafe(flags, state); |
101 | 19.8k | in_addr_hash_func(&route->prefsrc, route->family, state); |
102 | | |
103 | | /* nexthops (id, number of nexthops, nexthop) */ |
104 | 19.8k | route_nexthops_hash_func(route, state); |
105 | | |
106 | | /* metrics */ |
107 | 19.8k | route_metric_hash_func(&route->metric, state); |
108 | 19.8k | break; |
109 | | |
110 | 12.2k | case AF_INET6: |
111 | | /* First, table and destination prefix are used for classifying routes. |
112 | | * See fib6_add() and fib6_add_1() in net/ipv6/ip6_fib.c of the kernel. */ |
113 | 12.2k | siphash24_compress_typesafe(route->table, state); |
114 | 12.2k | in_addr_hash_func(&route->dst, route->family, state); |
115 | 12.2k | siphash24_compress_typesafe(route->dst_prefixlen, state); |
116 | | |
117 | | /* Then, source prefix is used. See fib6_add(). */ |
118 | 12.2k | in_addr_hash_func(&route->src, route->family, state); |
119 | 12.2k | siphash24_compress_typesafe(route->src_prefixlen, state); |
120 | | |
121 | | /* See fib6_add_rt2node(). */ |
122 | 12.2k | siphash24_compress_typesafe(route->priority, state); |
123 | | |
124 | | /* See rt6_duplicate_nexthop() in include/net/ip6_route.h of the kernel. |
125 | | * Here, we hash nexthop in a similar way as the one for IPv4. */ |
126 | 12.2k | route_nexthops_hash_func(route, state); |
127 | | |
128 | | /* Unlike IPv4 routes, metrics are not taken into account. */ |
129 | 12.2k | break; |
130 | | |
131 | 0 | default: |
132 | 0 | assert_not_reached(); |
133 | 32.1k | } |
134 | 32.1k | } |
135 | | |
136 | 18.5k | static int route_compare_func(const Route *a, const Route *b) { |
137 | 18.5k | int r; |
138 | | |
139 | 18.5k | r = CMP(a->family, b->family); |
140 | 18.5k | if (r != 0) |
141 | 1.27k | return r; |
142 | | |
143 | 17.2k | switch (a->family) { |
144 | 6.89k | case AF_INET: |
145 | 6.89k | r = CMP(a->table, b->table); |
146 | 6.89k | if (r != 0) |
147 | 2.35k | return r; |
148 | | |
149 | 4.54k | r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family)); |
150 | 4.54k | if (r != 0) |
151 | 1.80k | return r; |
152 | | |
153 | 2.73k | r = CMP(a->dst_prefixlen, b->dst_prefixlen); |
154 | 2.73k | if (r != 0) |
155 | 189 | return r; |
156 | | |
157 | 2.55k | r = CMP(a->priority, b->priority); |
158 | 2.55k | if (r != 0) |
159 | 164 | return r; |
160 | | |
161 | 2.38k | r = CMP(a->tos, b->tos); |
162 | 2.38k | if (r != 0) |
163 | 0 | return r; |
164 | | |
165 | 2.38k | r = CMP(a->protocol, b->protocol); |
166 | 2.38k | if (r != 0) |
167 | 0 | return r; |
168 | | |
169 | 2.38k | r = CMP(a->scope, b->scope); |
170 | 2.38k | if (r != 0) |
171 | 0 | return r; |
172 | | |
173 | 2.38k | r = CMP(a->type, b->type); |
174 | 2.38k | if (r != 0) |
175 | 0 | return r; |
176 | | |
177 | 2.38k | r = CMP(a->flags & ~RTNH_COMPARE_MASK, b->flags & ~RTNH_COMPARE_MASK); |
178 | 2.38k | if (r != 0) |
179 | 0 | return r; |
180 | | |
181 | 2.38k | r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family)); |
182 | 2.38k | if (r != 0) |
183 | 0 | return r; |
184 | | |
185 | 2.38k | r = route_nexthops_compare_func(a, b); |
186 | 2.38k | if (r != 0) |
187 | 0 | return r; |
188 | | |
189 | 2.38k | return route_metric_compare_func(&a->metric, &b->metric); |
190 | | |
191 | 10.3k | case AF_INET6: |
192 | 10.3k | r = CMP(a->table, b->table); |
193 | 10.3k | if (r != 0) |
194 | 564 | return r; |
195 | | |
196 | 9.78k | r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family)); |
197 | 9.78k | if (r != 0) |
198 | 273 | return r; |
199 | | |
200 | 9.51k | r = CMP(a->dst_prefixlen, b->dst_prefixlen); |
201 | 9.51k | if (r != 0) |
202 | 248 | return r; |
203 | | |
204 | 9.26k | r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family)); |
205 | 9.26k | if (r != 0) |
206 | 0 | return r; |
207 | | |
208 | 9.26k | r = CMP(a->src_prefixlen, b->src_prefixlen); |
209 | 9.26k | if (r != 0) |
210 | 0 | return r; |
211 | | |
212 | 9.26k | r = CMP(a->priority, b->priority); |
213 | 9.26k | if (r != 0) |
214 | 477 | return r; |
215 | | |
216 | 8.78k | return route_nexthops_compare_func(a, b); |
217 | | |
218 | 0 | default: |
219 | 0 | assert_not_reached(); |
220 | 17.2k | } |
221 | 17.2k | } |
222 | | |
223 | | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( |
224 | | route_hash_ops, |
225 | | Route, |
226 | | route_hash_func, |
227 | | route_compare_func, |
228 | | route_detach); |
229 | | |
230 | | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( |
231 | | route_hash_ops_unref, |
232 | | Route, |
233 | | route_hash_func, |
234 | | route_compare_func, |
235 | | route_unref); |
236 | | |
237 | | DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( |
238 | | route_section_hash_ops, |
239 | | ConfigSection, |
240 | | config_section_hash_func, |
241 | | config_section_compare_func, |
242 | | Route, |
243 | | route_detach); |
244 | | |
245 | 53.1k | int route_new(Route **ret) { |
246 | 53.1k | _cleanup_(route_unrefp) Route *route = NULL; |
247 | | |
248 | 53.1k | assert(ret); |
249 | | |
250 | 53.1k | route = new(Route, 1); |
251 | 53.1k | if (!route) |
252 | 0 | return -ENOMEM; |
253 | | |
254 | 53.1k | *route = (Route) { |
255 | 53.1k | .n_ref = 1, |
256 | 53.1k | .family = AF_UNSPEC, |
257 | 53.1k | .scope = RT_SCOPE_UNIVERSE, |
258 | 53.1k | .protocol = RTPROT_UNSPEC, |
259 | 53.1k | .type = RTN_UNICAST, |
260 | 53.1k | .table = RT_TABLE_MAIN, |
261 | 53.1k | .lifetime_usec = USEC_INFINITY, |
262 | 53.1k | .gateway_onlink = -1, |
263 | 53.1k | }; |
264 | | |
265 | 53.1k | *ret = TAKE_PTR(route); |
266 | | |
267 | 53.1k | return 0; |
268 | 53.1k | } |
269 | | |
270 | 59.5k | int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) { |
271 | 59.5k | _cleanup_(config_section_freep) ConfigSection *n = NULL; |
272 | 59.5k | _cleanup_(route_unrefp) Route *route = NULL; |
273 | 59.5k | int r; |
274 | | |
275 | 59.5k | assert(network); |
276 | 59.5k | assert(ret); |
277 | 59.5k | assert(filename); |
278 | 59.5k | assert(section_line > 0); |
279 | | |
280 | 59.5k | r = config_section_new(filename, section_line, &n); |
281 | 59.5k | if (r < 0) |
282 | 0 | return r; |
283 | | |
284 | 59.5k | route = hashmap_get(network->routes_by_section, n); |
285 | 59.5k | if (route) { |
286 | 23.8k | *ret = TAKE_PTR(route); |
287 | 23.8k | return 0; |
288 | 23.8k | } |
289 | | |
290 | 35.6k | if (hashmap_size(network->routes_by_section) >= routes_max()) |
291 | 0 | return -E2BIG; |
292 | | |
293 | 35.6k | r = route_new(&route); |
294 | 35.6k | if (r < 0) |
295 | 0 | return r; |
296 | | |
297 | 35.6k | route->protocol = RTPROT_STATIC; |
298 | 35.6k | route->network = network; |
299 | 35.6k | route->section = TAKE_PTR(n); |
300 | 35.6k | route->source = NETWORK_CONFIG_SOURCE_STATIC; |
301 | | |
302 | 35.6k | r = hashmap_ensure_put(&network->routes_by_section, &route_section_hash_ops, route->section, route); |
303 | 35.6k | if (r < 0) |
304 | 0 | return r; |
305 | | |
306 | 35.6k | *ret = TAKE_PTR(route); |
307 | 35.6k | return 0; |
308 | 35.6k | } |
309 | | |
310 | 0 | static int route_attach(Manager *manager, Route *route) { |
311 | 0 | int r; |
312 | |
|
313 | 0 | assert(manager); |
314 | 0 | assert(route); |
315 | 0 | assert(!route->network); |
316 | 0 | assert(!route->wireguard); |
317 | |
|
318 | 0 | r = set_ensure_put(&manager->routes, &route_hash_ops, route); |
319 | 0 | if (r < 0) |
320 | 0 | return r; |
321 | 0 | if (r == 0) |
322 | 0 | return -EEXIST; |
323 | | |
324 | 0 | route->manager = manager; |
325 | 0 | return 0; |
326 | 0 | } |
327 | | |
328 | 0 | int route_get(Manager *manager, const Route *route, Route **ret) { |
329 | 0 | Route *existing; |
330 | |
|
331 | 0 | assert(manager); |
332 | 0 | assert(route); |
333 | |
|
334 | 0 | existing = set_get(manager->routes, route); |
335 | 0 | if (!existing) |
336 | 0 | return -ENOENT; |
337 | | |
338 | 0 | if (ret) |
339 | 0 | *ret = existing; |
340 | |
|
341 | 0 | return 0; |
342 | 0 | } |
343 | | |
344 | 0 | static int route_get_link(Manager *manager, const Route *route, Link **ret) { |
345 | 0 | int r; |
346 | |
|
347 | 0 | assert(manager); |
348 | 0 | assert(route); |
349 | |
|
350 | 0 | if (route->nexthop_id != 0) { |
351 | 0 | NextHop *nh; |
352 | |
|
353 | 0 | r = nexthop_get_by_id(manager, route->nexthop_id, &nh); |
354 | 0 | if (r < 0) |
355 | 0 | return r; |
356 | | |
357 | 0 | return link_get_by_index(manager, nh->ifindex, ret); |
358 | 0 | } |
359 | | |
360 | 0 | return route_nexthop_get_link(manager, &route->nexthop, ret); |
361 | 0 | } |
362 | | |
363 | 0 | bool route_is_bound_to_link(const Route *route, Link *link) { |
364 | 0 | assert(route); |
365 | 0 | assert(link); |
366 | 0 | assert(link->manager); |
367 | |
|
368 | 0 | Link *route_link; |
369 | 0 | if (route_get_link(link->manager, route, &route_link) < 0) |
370 | 0 | return false; |
371 | | |
372 | 0 | return route_link->ifindex == link->ifindex; |
373 | 0 | } |
374 | | |
375 | 0 | int route_get_request(Manager *manager, const Route *route, Request **ret) { |
376 | 0 | Request *req; |
377 | |
|
378 | 0 | assert(manager); |
379 | 0 | assert(route); |
380 | |
|
381 | 0 | req = ordered_set_get(manager->request_queue, |
382 | 0 | &(const Request) { |
383 | 0 | .type = REQUEST_TYPE_ROUTE, |
384 | 0 | .userdata = (void*) route, |
385 | 0 | .hash_func = (hash_func_t) route_hash_func, |
386 | 0 | .compare_func = (compare_func_t) route_compare_func, |
387 | 0 | }); |
388 | 0 | if (!req) |
389 | 0 | return -ENOENT; |
390 | | |
391 | 0 | if (ret) |
392 | 0 | *ret = req; |
393 | 0 | return 0; |
394 | 0 | } |
395 | | |
396 | 0 | int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) { |
397 | 0 | _cleanup_(route_unrefp) Route *dest = NULL; |
398 | 0 | int r; |
399 | |
|
400 | 0 | assert(src); |
401 | 0 | assert(ret); |
402 | |
|
403 | 0 | dest = newdup(Route, src, 1); |
404 | 0 | if (!dest) |
405 | 0 | return -ENOMEM; |
406 | | |
407 | | /* Unset number of reference and all pointers */ |
408 | 0 | dest->n_ref = 1; |
409 | 0 | dest->manager = NULL; |
410 | 0 | dest->network = NULL; |
411 | 0 | dest->wireguard = NULL; |
412 | 0 | dest->section = NULL; |
413 | 0 | dest->nexthop = ROUTE_NEXTHOP_NULL; |
414 | 0 | dest->nexthops = NULL; |
415 | 0 | dest->metric = ROUTE_METRIC_NULL; |
416 | 0 | dest->expire = NULL; |
417 | |
|
418 | 0 | r = route_nexthops_copy(src, nh, dest); |
419 | 0 | if (r < 0) |
420 | 0 | return r; |
421 | | |
422 | 0 | r = route_metric_copy(&src->metric, &dest->metric); |
423 | 0 | if (r < 0) |
424 | 0 | return r; |
425 | | |
426 | 0 | *ret = TAKE_PTR(dest); |
427 | 0 | return 0; |
428 | 0 | } |
429 | | |
430 | 0 | static int route_to_string(const Route *route, Manager *manager, char **ret) { |
431 | 0 | _cleanup_free_ char *nexthop = NULL, *prefsrc = NULL, |
432 | 0 | *table = NULL, *scope = NULL, *proto = NULL, *flags = NULL; |
433 | 0 | const char *dst, *src; |
434 | |
|
435 | 0 | assert(route); |
436 | 0 | assert(manager); |
437 | 0 | assert(ret); |
438 | |
|
439 | 0 | dst = in_addr_is_set(route->family, &route->dst) || route->dst_prefixlen > 0 ? |
440 | 0 | IN_ADDR_PREFIX_TO_STRING(route->family, &route->dst, route->dst_prefixlen) : NULL; |
441 | 0 | src = in_addr_is_set(route->family, &route->src) || route->src_prefixlen > 0 ? |
442 | 0 | IN_ADDR_PREFIX_TO_STRING(route->family, &route->src, route->src_prefixlen) : NULL; |
443 | |
|
444 | 0 | (void) route_nexthops_to_string(route, &nexthop); |
445 | |
|
446 | 0 | if (in_addr_is_set(route->family, &route->prefsrc)) |
447 | 0 | (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc); |
448 | 0 | (void) route_scope_to_string_alloc(route->scope, &scope); |
449 | 0 | (void) manager_get_route_table_to_string(manager, route->table, /* append_num= */ true, &table); |
450 | 0 | (void) route_protocol_full_to_string_alloc(route->protocol, &proto); |
451 | 0 | (void) route_flags_to_string_alloc(route->flags, &flags); |
452 | |
|
453 | 0 | if (asprintf(ret, |
454 | 0 | "dst: %s, src: %s, %s, prefsrc: %s, " |
455 | 0 | "table: %s, priority: %"PRIu32", " |
456 | 0 | "proto: %s, scope: %s, " |
457 | 0 | "type: %s, flags: %s", |
458 | 0 | strna(dst), strna(src), strna(nexthop), strna(prefsrc), |
459 | 0 | strna(table), route->priority, |
460 | 0 | strna(proto), strna(scope), |
461 | 0 | strna(route_type_to_string(route->type)), strna(flags)) < 0) |
462 | 0 | return -ENOMEM; |
463 | | |
464 | 0 | return 0; |
465 | 0 | } |
466 | | |
467 | 0 | void log_route_debug(const Route *route, const char *str, Manager *manager) { |
468 | 0 | _cleanup_free_ char *state = NULL, *route_str = NULL; |
469 | 0 | Link *link = NULL; |
470 | |
|
471 | 0 | assert(route); |
472 | 0 | assert(str); |
473 | 0 | assert(manager); |
474 | |
|
475 | 0 | if (!DEBUG_LOGGING) |
476 | 0 | return; |
477 | | |
478 | 0 | (void) route_get_link(manager, route, &link); |
479 | 0 | (void) network_config_state_to_string_alloc(route->state, &state); |
480 | 0 | (void) route_to_string(route, manager, &route_str); |
481 | |
|
482 | 0 | log_link_debug(link, "%s %s route (%s): %s", |
483 | 0 | str, strna(network_config_source_to_string(route->source)), strna(state), strna(route_str)); |
484 | 0 | } |
485 | | |
486 | 0 | static void route_forget(Manager *manager, Route *route, const char *msg) { |
487 | 0 | assert(manager); |
488 | 0 | assert(route); |
489 | 0 | assert(msg); |
490 | |
|
491 | 0 | Request *req; |
492 | 0 | if (route_get_request(manager, route, &req) >= 0) |
493 | 0 | route_enter_removed(req->userdata); |
494 | |
|
495 | 0 | if (!route->manager && route_get(manager, route, &route) < 0) |
496 | 0 | return; |
497 | | |
498 | 0 | route_enter_removed(route); |
499 | 0 | log_route_debug(route, msg, manager); |
500 | 0 | route_detach(route); |
501 | 0 | } |
502 | | |
503 | 0 | static int route_set_netlink_message(const Route *route, sd_netlink_message *m) { |
504 | 0 | int r; |
505 | |
|
506 | 0 | assert(route); |
507 | 0 | assert(m); |
508 | | |
509 | | /* rtmsg header (and relevant attributes) */ |
510 | 0 | if (route->dst_prefixlen > 0) { |
511 | 0 | r = netlink_message_append_in_addr_union(m, RTA_DST, route->family, &route->dst); |
512 | 0 | if (r < 0) |
513 | 0 | return r; |
514 | | |
515 | 0 | r = sd_rtnl_message_route_set_dst_prefixlen(m, route->dst_prefixlen); |
516 | 0 | if (r < 0) |
517 | 0 | return r; |
518 | 0 | } |
519 | | |
520 | 0 | if (route->src_prefixlen > 0) { |
521 | 0 | r = netlink_message_append_in_addr_union(m, RTA_SRC, route->family, &route->src); |
522 | 0 | if (r < 0) |
523 | 0 | return r; |
524 | | |
525 | 0 | r = sd_rtnl_message_route_set_src_prefixlen(m, route->src_prefixlen); |
526 | 0 | if (r < 0) |
527 | 0 | return r; |
528 | 0 | } |
529 | | |
530 | 0 | r = sd_rtnl_message_route_set_tos(m, route->tos); |
531 | 0 | if (r < 0) |
532 | 0 | return r; |
533 | | |
534 | 0 | r = sd_rtnl_message_route_set_scope(m, route->scope); |
535 | 0 | if (r < 0) |
536 | 0 | return r; |
537 | | |
538 | 0 | r = sd_rtnl_message_route_set_type(m, route->type); |
539 | 0 | if (r < 0) |
540 | 0 | return r; |
541 | | |
542 | 0 | r = sd_rtnl_message_route_set_flags(m, route->flags & ~RTNH_COMPARE_MASK); |
543 | 0 | if (r < 0) |
544 | 0 | return r; |
545 | | |
546 | | /* attributes */ |
547 | 0 | r = sd_netlink_message_append_u32(m, RTA_PRIORITY, route->priority); |
548 | 0 | if (r < 0) |
549 | 0 | return r; |
550 | | |
551 | 0 | if (in_addr_is_set(route->family, &route->prefsrc)) { |
552 | 0 | r = netlink_message_append_in_addr_union(m, RTA_PREFSRC, route->family, &route->prefsrc); |
553 | 0 | if (r < 0) |
554 | 0 | return r; |
555 | 0 | } |
556 | | |
557 | 0 | if (route->table < 256) { |
558 | 0 | r = sd_rtnl_message_route_set_table(m, route->table); |
559 | 0 | if (r < 0) |
560 | 0 | return r; |
561 | 0 | } else { |
562 | 0 | r = sd_rtnl_message_route_set_table(m, RT_TABLE_UNSPEC); |
563 | 0 | if (r < 0) |
564 | 0 | return r; |
565 | | |
566 | | /* Table attribute to allow more than 256. */ |
567 | 0 | r = sd_netlink_message_append_u32(m, RTA_TABLE, route->table); |
568 | 0 | if (r < 0) |
569 | 0 | return r; |
570 | 0 | } |
571 | | |
572 | 0 | r = sd_netlink_message_append_u8(m, RTA_PREF, route->pref); |
573 | 0 | if (r < 0) |
574 | 0 | return r; |
575 | | |
576 | | /* nexthops */ |
577 | 0 | r = route_nexthops_set_netlink_message(route, m); |
578 | 0 | if (r < 0) |
579 | 0 | return r; |
580 | | |
581 | | /* metrics */ |
582 | 0 | r = route_metric_set_netlink_message(&route->metric, m); |
583 | 0 | if (r < 0) |
584 | 0 | return r; |
585 | | |
586 | 0 | return 0; |
587 | 0 | } |
588 | | |
589 | 0 | static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, RemoveRequest *rreq) { |
590 | 0 | int r; |
591 | |
|
592 | 0 | assert(m); |
593 | 0 | assert(rreq); |
594 | |
|
595 | 0 | Manager *manager = ASSERT_PTR(rreq->manager); |
596 | 0 | Route *route = ASSERT_PTR(rreq->userdata); |
597 | |
|
598 | 0 | r = sd_netlink_message_get_errno(m); |
599 | 0 | if (r < 0) { |
600 | 0 | log_message_full_errno(m, |
601 | 0 | (r == -ESRCH || /* the route is already removed? */ |
602 | 0 | (r == -EINVAL && route->nexthop_id != 0) || /* The nexthop is already removed? */ |
603 | 0 | !route->manager) ? /* already detached? */ |
604 | 0 | LOG_DEBUG : LOG_WARNING, |
605 | 0 | r, "Could not drop route, ignoring"); |
606 | | |
607 | | /* If the route cannot be removed, then assume the route is already removed. */ |
608 | 0 | route_forget(manager, route, "Forgetting"); |
609 | 0 | } |
610 | |
|
611 | 0 | return 1; |
612 | 0 | } |
613 | | |
614 | 0 | int route_remove(Route *route, Manager *manager) { |
615 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
616 | 0 | Link *link = NULL; |
617 | 0 | int r; |
618 | |
|
619 | 0 | assert(route); |
620 | 0 | assert(manager); |
621 | |
|
622 | 0 | if (manager->state == MANAGER_STOPPED) |
623 | 0 | return 0; /* The remove request will not be queued anyway. Suppress logging below. */ |
624 | | |
625 | | /* If the route is remembered, then use the remembered object. */ |
626 | 0 | (void) route_get(manager, route, &route); |
627 | |
|
628 | 0 | log_route_debug(route, "Removing", manager); |
629 | | |
630 | | /* For logging. */ |
631 | 0 | (void) route_get_link(manager, route, &link); |
632 | |
|
633 | 0 | r = sd_rtnl_message_new_route(manager->rtnl, &m, RTM_DELROUTE, route->family, route->protocol); |
634 | 0 | if (r < 0) |
635 | 0 | return log_link_warning_errno(link, r, "Could not create netlink message: %m"); |
636 | | |
637 | 0 | r = route_set_netlink_message(route, m); |
638 | 0 | if (r < 0) |
639 | 0 | return log_link_warning_errno(link, r, "Could not fill netlink message: %m"); |
640 | | |
641 | 0 | r = manager_remove_request_add(manager, route, route, manager->rtnl, m, route_remove_handler); |
642 | 0 | if (r < 0) |
643 | 0 | return log_link_warning_errno(link, r, "Could not queue rtnetlink message: %m"); |
644 | | |
645 | 0 | route_enter_removing(route); |
646 | 0 | return 0; |
647 | 0 | } |
648 | | |
649 | 0 | int route_remove_and_cancel(Route *route, Manager *manager) { |
650 | 0 | _cleanup_(request_unrefp) Request *req = NULL; |
651 | 0 | bool waiting = false; |
652 | |
|
653 | 0 | assert(route); |
654 | 0 | assert(manager); |
655 | | |
656 | | /* If the route is remembered by the manager, then use the remembered object. */ |
657 | 0 | (void) route_get(manager, route, &route); |
658 | | |
659 | | /* Cancel the request for the route. If the request is already called but we have not received the |
660 | | * notification about the request, then explicitly remove the route. */ |
661 | 0 | if (route_get_request(manager, route, &req) >= 0) { |
662 | 0 | request_ref(req); /* avoid the request freed by request_detach() */ |
663 | 0 | waiting = req->waiting_reply; |
664 | 0 | request_detach(req); |
665 | 0 | route_cancel_requesting(route); |
666 | 0 | } |
667 | | |
668 | | /* If we know that the route will come or already exists, remove it. */ |
669 | 0 | if (waiting || (route->manager && route_exists(route))) |
670 | 0 | return route_remove(route, manager); |
671 | | |
672 | 0 | return 0; |
673 | 0 | } |
674 | | |
675 | 0 | static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { |
676 | 0 | Route *route = ASSERT_PTR(userdata); |
677 | 0 | int r; |
678 | |
|
679 | 0 | if (!route->manager) |
680 | 0 | return 0; /* already detached. */ |
681 | | |
682 | 0 | r = route_remove(route, route->manager); |
683 | 0 | if (r < 0) { |
684 | 0 | Link *link = NULL; |
685 | 0 | (void) route_get_link(route->manager, route, &link); |
686 | 0 | log_link_warning_errno(link, r, "Could not remove route: %m"); |
687 | 0 | if (link) |
688 | 0 | link_enter_failed(link); |
689 | 0 | } |
690 | |
|
691 | 0 | return 1; |
692 | 0 | } |
693 | | |
694 | 0 | static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo) { |
695 | 0 | int r; |
696 | |
|
697 | 0 | assert(route); |
698 | |
|
699 | 0 | if (cacheinfo && cacheinfo->rta_expires != 0) |
700 | 0 | route->expiration_managed_by_kernel = true; |
701 | |
|
702 | 0 | if (route->lifetime_usec == USEC_INFINITY || /* We do not request expiration for the route. */ |
703 | 0 | route->expiration_managed_by_kernel) { /* We have received nonzero expiration previously. The expiration is managed by the kernel. */ |
704 | 0 | route->expire = sd_event_source_disable_unref(route->expire); |
705 | 0 | return 0; |
706 | 0 | } |
707 | | |
708 | 0 | Manager *manager = ASSERT_PTR(route->manager); |
709 | 0 | r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME, |
710 | 0 | route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true); |
711 | 0 | if (r < 0) { |
712 | 0 | Link *link = NULL; |
713 | 0 | (void) route_get_link(manager, route, &link); |
714 | 0 | return log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m"); |
715 | 0 | } |
716 | | |
717 | 0 | log_route_debug(route, "Configured expiration timer for", manager); |
718 | 0 | return 1; |
719 | 0 | } |
720 | | |
721 | 0 | static int route_update_by_request(Route *route, Request *req) { |
722 | 0 | assert(route); |
723 | 0 | assert(req); |
724 | |
|
725 | 0 | Route *rt = ASSERT_PTR(req->userdata); |
726 | |
|
727 | 0 | route->provider = rt->provider; |
728 | 0 | route->source = rt->source; |
729 | 0 | route->lifetime_usec = rt->lifetime_usec; |
730 | |
|
731 | 0 | return 0; |
732 | 0 | } |
733 | | |
734 | 0 | static int route_update_on_existing_one(Request *req, Route *requested) { |
735 | 0 | Manager *manager = ASSERT_PTR(ASSERT_PTR(req)->manager); |
736 | 0 | Route *existing; |
737 | 0 | int r; |
738 | |
|
739 | 0 | assert(requested); |
740 | |
|
741 | 0 | if (route_get(manager, requested, &existing) < 0) |
742 | 0 | return 0; |
743 | | |
744 | 0 | r = route_update_by_request(existing, req); |
745 | 0 | if (r < 0) |
746 | 0 | return r; |
747 | | |
748 | 0 | r = route_setup_timer(existing, NULL); |
749 | 0 | if (r < 0) |
750 | 0 | return r; |
751 | | |
752 | | /* This may be a bug in the kernel, but the MTU of an IPv6 route can be updated only when the |
753 | | * route has an expiration timer managed by the kernel (not by us). See fib6_add_rt2node() in |
754 | | * net/ipv6/ip6_fib.c of the kernel. */ |
755 | 0 | if (existing->family == AF_INET6 && |
756 | 0 | existing->expiration_managed_by_kernel) { |
757 | 0 | r = route_metric_set(&existing->metric, RTAX_MTU, route_metric_get(&requested->metric, RTAX_MTU)); |
758 | 0 | if (r < 0) |
759 | 0 | return r; |
760 | 0 | } |
761 | | |
762 | 0 | route_enter_configured(existing); |
763 | 0 | return 0; |
764 | 0 | } |
765 | | |
766 | 0 | static int route_update_on_existing(Request *req) { |
767 | 0 | Route *rt = ASSERT_PTR(ASSERT_PTR(req)->userdata); |
768 | 0 | int r; |
769 | |
|
770 | 0 | if (!req->manager) |
771 | | /* Already detached? At least there are two possibilities then. |
772 | | * 1) The interface is removed, and all queued requests for the interface are cancelled. |
773 | | * 2) networkd is now stopping, hence all queued requests are cancelled. |
774 | | * Anyway, we can ignore the request, and there is nothing we can do. */ |
775 | 0 | return 0; |
776 | | |
777 | 0 | if (rt->family == AF_INET || ordered_set_isempty(rt->nexthops)) |
778 | 0 | return route_update_on_existing_one(req, rt); |
779 | | |
780 | 0 | RouteNextHop *nh; |
781 | 0 | ORDERED_SET_FOREACH(nh, rt->nexthops) { |
782 | 0 | _cleanup_(route_unrefp) Route *dup = NULL; |
783 | |
|
784 | 0 | r = route_dup(rt, nh, &dup); |
785 | 0 | if (r < 0) |
786 | 0 | return r; |
787 | | |
788 | 0 | r = route_update_on_existing_one(req, dup); |
789 | 0 | if (r < 0) |
790 | 0 | return r; |
791 | 0 | } |
792 | | |
793 | 0 | return 0; |
794 | 0 | } |
795 | | |
796 | 0 | int route_configure_handler_internal(sd_netlink_message *m, Request *req, Route *route) { |
797 | 0 | int r; |
798 | |
|
799 | 0 | assert(m); |
800 | 0 | assert(req); |
801 | 0 | assert(route); |
802 | |
|
803 | 0 | Link *link = ASSERT_PTR(req->link); |
804 | |
|
805 | 0 | r = sd_netlink_message_get_errno(m); |
806 | 0 | if (r == -EEXIST) { |
807 | | /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE notification, so |
808 | | * here we need to update the state, provider, source, timer, and so on. */ |
809 | 0 | r = route_update_on_existing(req); |
810 | 0 | if (r < 0) { |
811 | 0 | log_link_warning_errno(link, r, "Failed to update existing route: %m"); |
812 | 0 | link_enter_failed(link); |
813 | 0 | return 0; |
814 | 0 | } |
815 | | |
816 | 0 | return 1; |
817 | 0 | } |
818 | 0 | if (r < 0) { |
819 | 0 | _cleanup_free_ char *str = NULL; |
820 | 0 | (void) route_to_string(route, link->manager, &str); |
821 | 0 | log_link_message_warning_errno(link, m, r, "Failed to configure %s route (%s)", |
822 | 0 | network_config_source_to_string(route->source), strna(str)); |
823 | 0 | link_enter_failed(link); |
824 | 0 | return 0; |
825 | 0 | } |
826 | | |
827 | 0 | return 1; |
828 | 0 | } |
829 | | |
830 | 0 | static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) { |
831 | 0 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
832 | 0 | int r; |
833 | |
|
834 | 0 | assert(route); |
835 | 0 | assert(link); |
836 | 0 | assert(link->manager); |
837 | 0 | assert(req); |
838 | |
|
839 | 0 | log_route_debug(route, "Configuring", link->manager); |
840 | |
|
841 | 0 | r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol); |
842 | 0 | if (r < 0) |
843 | 0 | return r; |
844 | | |
845 | 0 | r = route_set_netlink_message(route, m); |
846 | 0 | if (r < 0) |
847 | 0 | return r; |
848 | | |
849 | 0 | if (lifetime_sec != UINT32_MAX) { |
850 | 0 | r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec); |
851 | 0 | if (r < 0) |
852 | 0 | return r; |
853 | 0 | } |
854 | | |
855 | 0 | return request_call_netlink_async(link->manager->rtnl, m, req); |
856 | 0 | } |
857 | | |
858 | 0 | static int route_requeue_request(Request *req, Link *link, const Route *route) { |
859 | 0 | _unused_ _cleanup_(request_unrefp) Request *req_unref = NULL; |
860 | 0 | _cleanup_(route_unrefp) Route *tmp = NULL; |
861 | 0 | int r; |
862 | |
|
863 | 0 | assert(req); |
864 | 0 | assert(link); |
865 | 0 | assert(link->manager); |
866 | 0 | assert(route); |
867 | | |
868 | | /* It is not possible to adjust the Route object owned by Request, as it is used as a key to manage |
869 | | * Request objects in the queue. Hence, we need to re-request with the updated Route object. */ |
870 | |
|
871 | 0 | if (!route_nexthops_needs_adjust(route)) |
872 | 0 | return 0; /* The Route object does not need the adjustment. Continue with it. */ |
873 | | |
874 | 0 | r = route_dup(route, NULL, &tmp); |
875 | 0 | if (r < 0) |
876 | 0 | return r; |
877 | | |
878 | 0 | r = route_adjust_nexthops(tmp, link); |
879 | 0 | if (r <= 0) |
880 | 0 | return r; |
881 | | |
882 | 0 | if (route_compare_func(route, tmp) == 0 && route->type == tmp->type) |
883 | 0 | return 0; /* No effective change?? That's OK. */ |
884 | | |
885 | | /* Avoid the request to be freed by request_detach(). */ |
886 | 0 | req_unref = request_ref(req); |
887 | | |
888 | | /* Detach the request from the queue, to make not the new request is deduped. |
889 | | * Why this is necessary? IPv6 routes with different type may be handled as the same, |
890 | | * As commented in route_adjust_nexthops(), we need to configure the adjusted type, |
891 | | * otherwise we cannot remove the route on reconfigure or so. If we request the new Route object |
892 | | * without detaching the current request, the new request is deduped, and the route is configured |
893 | | * with unmodified type. */ |
894 | 0 | request_detach(req); |
895 | | |
896 | | /* Request the route with the adjusted Route object combined with the same other parameters. */ |
897 | 0 | r = link_requeue_request(link, req, tmp, NULL); |
898 | 0 | if (r < 0) |
899 | 0 | return r; |
900 | 0 | if (r == 0) |
901 | 0 | return 1; /* Already queued?? That's OK. Maybe, [Route] section is effectively duplicated. */ |
902 | | |
903 | 0 | TAKE_PTR(tmp); |
904 | 0 | return 1; /* New request is queued. Finish to process this request. */ |
905 | 0 | } |
906 | | |
907 | 0 | static int route_is_ready_to_configure(const Route *route, Link *link) { |
908 | 0 | assert(route); |
909 | 0 | assert(link); |
910 | |
|
911 | 0 | if (!link_is_ready_to_configure(link, /* allow_unmanaged= */ false)) |
912 | 0 | return false; |
913 | | |
914 | 0 | if (in_addr_is_set(route->family, &route->prefsrc) > 0) { |
915 | 0 | Address *a; |
916 | |
|
917 | 0 | if (manager_get_address(link->manager, route->family, &route->prefsrc, &a) < 0) |
918 | 0 | return false; |
919 | | |
920 | 0 | if (!address_is_ready(a)) |
921 | 0 | return false; |
922 | 0 | } |
923 | | |
924 | 0 | return route_nexthops_is_ready_to_configure(route, link->manager); |
925 | 0 | } |
926 | | |
927 | 0 | static int route_process_request(Request *req, Link *link, Route *route) { |
928 | 0 | Route *existing; |
929 | 0 | int r; |
930 | |
|
931 | 0 | assert(req); |
932 | 0 | assert(link); |
933 | 0 | assert(link->manager); |
934 | 0 | assert(route); |
935 | |
|
936 | 0 | r = route_is_ready_to_configure(route, link); |
937 | 0 | if (r < 0) |
938 | 0 | return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m"); |
939 | 0 | if (r == 0) |
940 | 0 | return 0; |
941 | | |
942 | 0 | usec_t now_usec; |
943 | 0 | assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0); |
944 | 0 | uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec); |
945 | 0 | if (sec == 0) { |
946 | 0 | log_link_debug(link, "Refuse to configure %s route with zero lifetime.", |
947 | 0 | network_config_source_to_string(route->source)); |
948 | |
|
949 | 0 | route_cancel_requesting(route); |
950 | 0 | if (route_get(link->manager, route, &existing) >= 0) |
951 | 0 | route_cancel_requesting(existing); |
952 | 0 | return 1; |
953 | 0 | } |
954 | | |
955 | 0 | r = route_requeue_request(req, link, route); |
956 | 0 | if (r != 0) |
957 | 0 | return r; |
958 | | |
959 | 0 | r = route_configure(route, sec, link, req); |
960 | 0 | if (r < 0) |
961 | 0 | return log_link_warning_errno(link, r, "Failed to configure route: %m"); |
962 | | |
963 | 0 | route_enter_configuring(route); |
964 | 0 | if (route_get(link->manager, route, &existing) >= 0) |
965 | 0 | route_enter_configuring(existing); |
966 | 0 | return 1; |
967 | 0 | } |
968 | | |
969 | | static int link_request_route_one( |
970 | | Link *link, |
971 | | const Route *route, |
972 | | const RouteNextHop *nh, |
973 | | unsigned *message_counter, |
974 | 0 | route_netlink_handler_t netlink_handler) { |
975 | |
|
976 | 0 | _cleanup_(route_unrefp) Route *tmp = NULL; |
977 | 0 | Route *existing = NULL; |
978 | 0 | int r; |
979 | |
|
980 | 0 | assert(link); |
981 | 0 | assert(link->manager); |
982 | 0 | assert(route); |
983 | |
|
984 | 0 | r = route_dup(route, nh, &tmp); |
985 | 0 | if (r < 0) |
986 | 0 | return r; |
987 | | |
988 | 0 | r = route_adjust_nexthops(tmp, link); |
989 | 0 | if (r < 0) |
990 | 0 | return r; |
991 | | |
992 | 0 | if (route_get(link->manager, tmp, &existing) >= 0) |
993 | | /* Copy state for logging below. */ |
994 | 0 | tmp->state = existing->state; |
995 | |
|
996 | 0 | log_route_debug(tmp, "Requesting", link->manager); |
997 | 0 | r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE, |
998 | 0 | tmp, |
999 | 0 | route_unref, |
1000 | 0 | route_hash_func, |
1001 | 0 | route_compare_func, |
1002 | 0 | route_process_request, |
1003 | 0 | message_counter, |
1004 | 0 | netlink_handler, |
1005 | 0 | NULL); |
1006 | 0 | if (r <= 0) |
1007 | 0 | return r; |
1008 | | |
1009 | 0 | route_enter_requesting(tmp); |
1010 | 0 | if (existing) |
1011 | 0 | route_enter_requesting(existing); |
1012 | |
|
1013 | 0 | TAKE_PTR(tmp); |
1014 | 0 | return 1; |
1015 | 0 | } |
1016 | | |
1017 | | int link_request_route( |
1018 | | Link *link, |
1019 | | const Route *route, |
1020 | | unsigned *message_counter, |
1021 | 0 | route_netlink_handler_t netlink_handler) { |
1022 | |
|
1023 | 0 | int r; |
1024 | |
|
1025 | 0 | assert(link); |
1026 | 0 | assert(link->manager); |
1027 | 0 | assert(route); |
1028 | 0 | assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN); |
1029 | |
|
1030 | 0 | if (route->family == AF_INET || route_is_reject(route) || ordered_set_isempty(route->nexthops)) |
1031 | 0 | return link_request_route_one(link, route, NULL, message_counter, netlink_handler); |
1032 | | |
1033 | 0 | RouteNextHop *nh; |
1034 | 0 | ORDERED_SET_FOREACH(nh, route->nexthops) { |
1035 | 0 | r = link_request_route_one(link, route, nh, message_counter, netlink_handler); |
1036 | 0 | if (r < 0) |
1037 | 0 | return r; |
1038 | 0 | } |
1039 | | |
1040 | 0 | return 0; |
1041 | 0 | } |
1042 | | |
1043 | 0 | static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { |
1044 | 0 | int r; |
1045 | |
|
1046 | 0 | assert(req); |
1047 | 0 | assert(link); |
1048 | 0 | assert(route); |
1049 | |
|
1050 | 0 | r = route_configure_handler_internal(m, req, route); |
1051 | 0 | if (r <= 0) |
1052 | 0 | return r; |
1053 | | |
1054 | 0 | if (link->static_route_messages == 0) { |
1055 | 0 | log_link_debug(link, "Routes set"); |
1056 | 0 | link->static_routes_configured = true; |
1057 | 0 | link_check_ready(link); |
1058 | 0 | } |
1059 | |
|
1060 | 0 | return 1; |
1061 | 0 | } |
1062 | | |
1063 | 0 | static int link_request_wireguard_routes(Link *link, bool only_ipv4) { |
1064 | 0 | Route *route; |
1065 | 0 | int r; |
1066 | |
|
1067 | 0 | assert(link); |
1068 | |
|
1069 | 0 | if (!link->netdev || link->netdev->kind != NETDEV_KIND_WIREGUARD) |
1070 | 0 | return 0; |
1071 | | |
1072 | 0 | Wireguard *w = WIREGUARD(link->netdev); |
1073 | |
|
1074 | 0 | SET_FOREACH(route, w->routes) { |
1075 | 0 | if (only_ipv4 && route->family != AF_INET) |
1076 | 0 | continue; |
1077 | | |
1078 | 0 | r = link_request_route(link, route, &link->static_route_messages, static_route_handler); |
1079 | 0 | if (r < 0) |
1080 | 0 | return r; |
1081 | 0 | } |
1082 | | |
1083 | 0 | return 0; |
1084 | 0 | } |
1085 | | |
1086 | 0 | int link_request_static_routes(Link *link, bool only_ipv4) { |
1087 | 0 | Route *route; |
1088 | 0 | int r; |
1089 | |
|
1090 | 0 | assert(link); |
1091 | 0 | assert(link->network); |
1092 | |
|
1093 | 0 | link->static_routes_configured = false; |
1094 | |
|
1095 | 0 | HASHMAP_FOREACH(route, link->network->routes_by_section) { |
1096 | 0 | if (route->source != NETWORK_CONFIG_SOURCE_STATIC) |
1097 | 0 | continue; |
1098 | | |
1099 | 0 | if (only_ipv4 && route->family != AF_INET) |
1100 | 0 | continue; |
1101 | | |
1102 | 0 | r = link_request_route(link, route, &link->static_route_messages, static_route_handler); |
1103 | 0 | if (r < 0) |
1104 | 0 | return r; |
1105 | 0 | } |
1106 | | |
1107 | 0 | r = link_request_wireguard_routes(link, only_ipv4); |
1108 | 0 | if (r < 0) |
1109 | 0 | return r; |
1110 | | |
1111 | 0 | if (link->static_route_messages == 0) { |
1112 | 0 | link->static_routes_configured = true; |
1113 | 0 | link_check_ready(link); |
1114 | 0 | } else { |
1115 | 0 | log_link_debug(link, "Requesting routes"); |
1116 | 0 | link_set_state(link, LINK_STATE_CONFIGURING); |
1117 | 0 | } |
1118 | |
|
1119 | 0 | return 0; |
1120 | 0 | } |
1121 | | |
1122 | | static int process_route_one( |
1123 | | Manager *manager, |
1124 | | uint16_t type, |
1125 | | Route *tmp, |
1126 | 0 | const struct rta_cacheinfo *cacheinfo) { |
1127 | |
|
1128 | 0 | Route *route = NULL; |
1129 | 0 | Link *link = NULL; |
1130 | 0 | bool is_new = false, update_dhcp4; |
1131 | 0 | int r; |
1132 | |
|
1133 | 0 | assert(manager); |
1134 | 0 | assert(tmp); |
1135 | 0 | assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)); |
1136 | |
|
1137 | 0 | (void) route_get(manager, tmp, &route); |
1138 | 0 | (void) route_get_link(manager, tmp, &link); |
1139 | |
|
1140 | 0 | update_dhcp4 = link && tmp->family == AF_INET6 && tmp->dst_prefixlen == 0; |
1141 | |
|
1142 | 0 | switch (type) { |
1143 | 0 | case RTM_NEWROUTE: { |
1144 | 0 | Request *req = NULL; |
1145 | 0 | (void) route_get_request(manager, tmp, &req); |
1146 | |
|
1147 | 0 | if (!route) { |
1148 | 0 | if (!manager->manage_foreign_routes && !(req && req->waiting_reply)) { |
1149 | 0 | route_enter_configured(tmp); |
1150 | 0 | log_route_debug(tmp, "Ignoring received", manager); |
1151 | 0 | return 0; |
1152 | 0 | } |
1153 | | |
1154 | | /* If we do not know the route, then save it. */ |
1155 | 0 | r = route_attach(manager, tmp); |
1156 | 0 | if (r < 0) { |
1157 | 0 | log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m"); |
1158 | 0 | return 0; |
1159 | 0 | } |
1160 | | |
1161 | 0 | route = route_ref(tmp); |
1162 | 0 | is_new = true; |
1163 | |
|
1164 | 0 | } else { |
1165 | | /* Update remembered route with the received notification. */ |
1166 | | |
1167 | | /* Here, update weight only when a non-zero weight is received. As the kernel does |
1168 | | * not provide the weight of a single-path route. In such case, tmp->nexthop.weight |
1169 | | * is zero, hence we should not overwrite the known weight of the route. */ |
1170 | 0 | if (tmp->nexthop.weight != 0) |
1171 | 0 | route->nexthop.weight = tmp->nexthop.weight; |
1172 | 0 | } |
1173 | | |
1174 | | /* Also update information that cannot be obtained through netlink notification. */ |
1175 | 0 | if (req && req->waiting_reply) { |
1176 | 0 | r = route_update_by_request(route, req); |
1177 | 0 | if (r < 0) { |
1178 | 0 | log_link_warning_errno(link, r, "Failed to update route by request: %m"); |
1179 | 0 | link_enter_failed(link); |
1180 | 0 | return 0; |
1181 | 0 | } |
1182 | | |
1183 | | /* We configure IPv6 multipath route separately. When the first path is configured, |
1184 | | * the kernel does not provide the weight of the path. So, we need to adjust it here. |
1185 | | * Hopefully, the weight is assigned correctly. */ |
1186 | 0 | if (route->nexthop.weight == 0) { |
1187 | 0 | Route *rt = ASSERT_PTR(req->userdata); |
1188 | 0 | route->nexthop.weight = rt->nexthop.weight; |
1189 | 0 | } |
1190 | 0 | } |
1191 | | |
1192 | 0 | route_attach_to_nexthop(route); |
1193 | |
|
1194 | 0 | route_enter_configured(route); |
1195 | 0 | log_route_debug(route, is_new ? "Received new" : "Received remembered", manager); |
1196 | |
|
1197 | 0 | (void) route_setup_timer(route, cacheinfo); |
1198 | |
|
1199 | 0 | break; |
1200 | 0 | } |
1201 | 0 | case RTM_DELROUTE: |
1202 | 0 | if (route) |
1203 | 0 | route_forget(manager, route, "Forgetting removed"); |
1204 | 0 | else |
1205 | 0 | log_route_debug(tmp, |
1206 | 0 | manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring removed", |
1207 | 0 | manager); |
1208 | 0 | break; |
1209 | | |
1210 | 0 | default: |
1211 | 0 | assert_not_reached(); |
1212 | 0 | } |
1213 | | |
1214 | 0 | if (update_dhcp4) { |
1215 | 0 | r = dhcp4_update_ipv6_connectivity(link); |
1216 | 0 | if (r < 0) { |
1217 | 0 | log_link_warning_errno(link, r, "Failed to notify IPv6 connectivity to DHCPv4 client: %m"); |
1218 | 0 | link_enter_failed(link); |
1219 | 0 | } |
1220 | 0 | } |
1221 | |
|
1222 | 0 | return 1; |
1223 | 0 | } |
1224 | | |
1225 | 0 | int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { |
1226 | 0 | _cleanup_(route_unrefp) Route *tmp = NULL; |
1227 | 0 | int r; |
1228 | |
|
1229 | 0 | assert(rtnl); |
1230 | 0 | assert(message); |
1231 | 0 | assert(m); |
1232 | |
|
1233 | 0 | if (sd_netlink_message_is_error(message)) { |
1234 | 0 | r = sd_netlink_message_get_errno(message); |
1235 | 0 | if (r < 0) |
1236 | 0 | log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring"); |
1237 | |
|
1238 | 0 | return 0; |
1239 | 0 | } |
1240 | | |
1241 | 0 | uint16_t type; |
1242 | 0 | r = sd_netlink_message_get_type(message, &type); |
1243 | 0 | if (r < 0) { |
1244 | 0 | log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); |
1245 | 0 | return 0; |
1246 | 0 | } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) { |
1247 | 0 | log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type); |
1248 | 0 | return 0; |
1249 | 0 | } |
1250 | | |
1251 | 0 | r = route_new(&tmp); |
1252 | 0 | if (r < 0) |
1253 | 0 | return log_oom(); |
1254 | | |
1255 | | /* rtmsg header */ |
1256 | 0 | r = sd_rtnl_message_route_get_family(message, &tmp->family); |
1257 | 0 | if (r < 0) { |
1258 | 0 | log_warning_errno(r, "rtnl: received route message without family, ignoring: %m"); |
1259 | 0 | return 0; |
1260 | 0 | } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) { |
1261 | 0 | log_debug("rtnl: received route message with invalid family '%i', ignoring.", tmp->family); |
1262 | 0 | return 0; |
1263 | 0 | } |
1264 | | |
1265 | 0 | r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen); |
1266 | 0 | if (r < 0) { |
1267 | 0 | log_warning_errno(r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m"); |
1268 | 0 | return 0; |
1269 | 0 | } |
1270 | | |
1271 | 0 | r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen); |
1272 | 0 | if (r < 0) { |
1273 | 0 | log_warning_errno(r, "rtnl: received route message with invalid source prefixlen, ignoring: %m"); |
1274 | 0 | return 0; |
1275 | 0 | } |
1276 | | |
1277 | 0 | r = sd_rtnl_message_route_get_tos(message, &tmp->tos); |
1278 | 0 | if (r < 0) { |
1279 | 0 | log_warning_errno(r, "rtnl: received route message with invalid tos, ignoring: %m"); |
1280 | 0 | return 0; |
1281 | 0 | } |
1282 | | |
1283 | 0 | r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol); |
1284 | 0 | if (r < 0) { |
1285 | 0 | log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m"); |
1286 | 0 | return 0; |
1287 | 0 | } |
1288 | | |
1289 | 0 | r = sd_rtnl_message_route_get_scope(message, &tmp->scope); |
1290 | 0 | if (r < 0) { |
1291 | 0 | log_warning_errno(r, "rtnl: received route message with invalid scope, ignoring: %m"); |
1292 | 0 | return 0; |
1293 | 0 | } |
1294 | | |
1295 | 0 | r = sd_rtnl_message_route_get_type(message, &tmp->type); |
1296 | 0 | if (r < 0) { |
1297 | 0 | log_warning_errno(r, "rtnl: received route message with invalid type, ignoring: %m"); |
1298 | 0 | return 0; |
1299 | 0 | } |
1300 | | |
1301 | 0 | r = sd_rtnl_message_route_get_flags(message, &tmp->flags); |
1302 | 0 | if (r < 0) { |
1303 | 0 | log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m"); |
1304 | 0 | return 0; |
1305 | 0 | } |
1306 | | |
1307 | | /* attributes */ |
1308 | 0 | r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst); |
1309 | 0 | if (r < 0 && r != -ENODATA) { |
1310 | 0 | log_warning_errno(r, "rtnl: received route message without valid destination, ignoring: %m"); |
1311 | 0 | return 0; |
1312 | 0 | } |
1313 | | |
1314 | 0 | r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src); |
1315 | 0 | if (r < 0 && r != -ENODATA) { |
1316 | 0 | log_warning_errno(r, "rtnl: received route message without valid source, ignoring: %m"); |
1317 | 0 | return 0; |
1318 | 0 | } |
1319 | | |
1320 | 0 | r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority); |
1321 | 0 | if (r < 0 && r != -ENODATA) { |
1322 | 0 | log_warning_errno(r, "rtnl: received route message with invalid priority, ignoring: %m"); |
1323 | 0 | return 0; |
1324 | 0 | } |
1325 | | |
1326 | 0 | r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc); |
1327 | 0 | if (r < 0 && r != -ENODATA) { |
1328 | 0 | log_warning_errno(r, "rtnl: received route message without valid preferred source, ignoring: %m"); |
1329 | 0 | return 0; |
1330 | 0 | } |
1331 | | |
1332 | 0 | r = sd_netlink_message_read_u32(message, RTA_TABLE, &tmp->table); |
1333 | 0 | if (r == -ENODATA) { |
1334 | 0 | unsigned char table; |
1335 | |
|
1336 | 0 | r = sd_rtnl_message_route_get_table(message, &table); |
1337 | 0 | if (r >= 0) |
1338 | 0 | tmp->table = table; |
1339 | 0 | } |
1340 | 0 | if (r < 0) { |
1341 | 0 | log_warning_errno(r, "rtnl: received route message with invalid table, ignoring: %m"); |
1342 | 0 | return 0; |
1343 | 0 | } |
1344 | | |
1345 | 0 | r = sd_netlink_message_read_u8(message, RTA_PREF, &tmp->pref); |
1346 | 0 | if (r < 0 && r != -ENODATA) { |
1347 | 0 | log_warning_errno(r, "rtnl: received route message with invalid preference, ignoring: %m"); |
1348 | 0 | return 0; |
1349 | 0 | } |
1350 | | |
1351 | | /* nexthops */ |
1352 | 0 | if (route_nexthops_read_netlink_message(tmp, message) < 0) |
1353 | 0 | return 0; |
1354 | | |
1355 | | /* metrics */ |
1356 | 0 | if (route_metric_read_netlink_message(&tmp->metric, message) < 0) |
1357 | 0 | return 0; |
1358 | | |
1359 | 0 | bool has_cacheinfo; |
1360 | 0 | struct rta_cacheinfo cacheinfo; |
1361 | 0 | r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo); |
1362 | 0 | if (r < 0 && r != -ENODATA) { |
1363 | 0 | log_warning_errno(r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m"); |
1364 | 0 | return 0; |
1365 | 0 | } |
1366 | 0 | has_cacheinfo = r >= 0; |
1367 | |
|
1368 | 0 | if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops)) |
1369 | 0 | return process_route_one(m, type, tmp, has_cacheinfo ? &cacheinfo : NULL); |
1370 | | |
1371 | 0 | RouteNextHop *nh; |
1372 | 0 | ORDERED_SET_FOREACH(nh, tmp->nexthops) { |
1373 | 0 | _cleanup_(route_unrefp) Route *dup = NULL; |
1374 | |
|
1375 | 0 | r = route_dup(tmp, nh, &dup); |
1376 | 0 | if (r < 0) |
1377 | 0 | return log_oom(); |
1378 | | |
1379 | 0 | r = process_route_one(m, type, dup, has_cacheinfo ? &cacheinfo : NULL); |
1380 | 0 | if (r < 0) |
1381 | 0 | return r; |
1382 | 0 | } |
1383 | | |
1384 | 0 | return 1; |
1385 | 0 | } |
1386 | | |
1387 | 0 | void manager_mark_routes(Manager *manager, Link *link, NetworkConfigSource source) { |
1388 | 0 | Route *route; |
1389 | |
|
1390 | 0 | assert(manager); |
1391 | |
|
1392 | 0 | SET_FOREACH(route, manager->routes) { |
1393 | 0 | if (route->source != source) |
1394 | 0 | continue; |
1395 | | |
1396 | 0 | if (link) { |
1397 | 0 | Link *route_link; |
1398 | |
|
1399 | 0 | if (route_get_link(manager, route, &route_link) < 0) |
1400 | 0 | continue; |
1401 | 0 | if (route_link != link) |
1402 | 0 | continue; |
1403 | 0 | } |
1404 | | |
1405 | 0 | route_mark(route); |
1406 | 0 | } |
1407 | 0 | } |
1408 | | |
1409 | 0 | static bool route_by_kernel(const Route *route) { |
1410 | 0 | assert(route); |
1411 | |
|
1412 | 0 | if (route->protocol == RTPROT_KERNEL) |
1413 | 0 | return true; |
1414 | | |
1415 | | /* The kernels older than a826b04303a40d52439aa141035fca5654ccaccd (v5.11) create the IPv6 |
1416 | | * multicast with RTPROT_BOOT. Do not touch it. */ |
1417 | 0 | if (route->protocol == RTPROT_BOOT && |
1418 | 0 | route->family == AF_INET6 && |
1419 | 0 | route->dst_prefixlen == 8 && |
1420 | 0 | in6_addr_equal(&route->dst.in6, & (struct in6_addr) {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}})) |
1421 | 0 | return true; |
1422 | | |
1423 | 0 | return false; |
1424 | 0 | } |
1425 | | |
1426 | 0 | bool route_can_update(Manager *manager, const Route *existing, const Route *requesting) { |
1427 | 0 | int r; |
1428 | |
|
1429 | 0 | assert(manager); |
1430 | 0 | assert(existing); |
1431 | 0 | assert(requesting); |
1432 | |
|
1433 | 0 | if (route_compare_func(existing, requesting) != 0) { |
1434 | 0 | log_route_debug(existing, "Cannot update route, as the existing route is different", manager); |
1435 | 0 | return false; |
1436 | 0 | } |
1437 | | |
1438 | 0 | switch (existing->family) { |
1439 | 0 | case AF_INET: |
1440 | 0 | if (existing->nexthop.weight != requesting->nexthop.weight) { |
1441 | 0 | log_debug("Cannot update route: existing weight: %u, requesting weight: %u", |
1442 | 0 | existing->nexthop.weight, requesting->nexthop.weight); |
1443 | 0 | return false; |
1444 | 0 | } |
1445 | 0 | return true; |
1446 | | |
1447 | 0 | case AF_INET6: |
1448 | 0 | if (existing->protocol != requesting->protocol) { |
1449 | 0 | if (DEBUG_LOGGING) { |
1450 | 0 | _cleanup_free_ char *ex = NULL, *req = NULL; |
1451 | |
|
1452 | 0 | r = route_protocol_to_string_alloc(existing->protocol, &ex); |
1453 | 0 | if (r < 0) |
1454 | 0 | return false; |
1455 | | |
1456 | 0 | r = route_protocol_to_string_alloc(requesting->protocol, &req); |
1457 | 0 | if (r < 0) |
1458 | 0 | return false; |
1459 | | |
1460 | 0 | log_debug("Cannot update route: existing protocol: %s, requesting protocol: %s", ex, req); |
1461 | 0 | } |
1462 | | |
1463 | 0 | return false; |
1464 | 0 | } |
1465 | 0 | if (existing->type != requesting->type) { |
1466 | 0 | log_debug("Cannot update route: existing type: %s, requesting type: %s", |
1467 | 0 | route_type_to_string(existing->type), |
1468 | 0 | route_type_to_string(requesting->type)); |
1469 | |
|
1470 | 0 | return false; |
1471 | 0 | } |
1472 | 0 | if ((existing->flags & ~RTNH_COMPARE_MASK) != (requesting->flags & ~RTNH_COMPARE_MASK)) { |
1473 | 0 | if (DEBUG_LOGGING) { |
1474 | 0 | _cleanup_free_ char *ex = NULL, *req = NULL; |
1475 | |
|
1476 | 0 | r = route_flags_to_string_alloc(existing->flags, &ex); |
1477 | 0 | if (r < 0) |
1478 | 0 | return false; |
1479 | | |
1480 | 0 | r = route_flags_to_string_alloc(requesting->flags, &req); |
1481 | 0 | if (r < 0) |
1482 | 0 | return false; |
1483 | | |
1484 | 0 | log_debug("Cannot update route: existing flags: %s, requesting flags: %s", ex, req); |
1485 | 0 | } |
1486 | | |
1487 | 0 | return false; |
1488 | 0 | } |
1489 | 0 | if (!in6_addr_equal(&existing->prefsrc.in6, &requesting->prefsrc.in6)) { |
1490 | 0 | log_debug("Cannot update route: existing preferred source: %s, requesting preferred source: %s", |
1491 | 0 | IN6_ADDR_TO_STRING(&existing->prefsrc.in6), |
1492 | 0 | IN6_ADDR_TO_STRING(&requesting->prefsrc.in6)); |
1493 | 0 | return false; |
1494 | 0 | } |
1495 | 0 | if (existing->pref != requesting->pref) { |
1496 | 0 | log_debug("Cannot update route: existing preference: %u, requesting preference: %u", |
1497 | 0 | existing->pref, requesting->pref); |
1498 | 0 | return false; |
1499 | 0 | } |
1500 | 0 | if (existing->expiration_managed_by_kernel && requesting->lifetime_usec == USEC_INFINITY) { |
1501 | 0 | log_route_debug(existing, |
1502 | 0 | "Cannot update route: the expiration is managed by the kernel and requested lifetime is infinite", |
1503 | 0 | manager); |
1504 | 0 | return false; /* We cannot disable expiration timer in the kernel. */ |
1505 | 0 | } |
1506 | 0 | if (!route_metric_can_update(&existing->metric, &requesting->metric, existing->expiration_managed_by_kernel)) { |
1507 | 0 | log_route_debug(existing, |
1508 | 0 | "Cannot update route: expiration is managed by the kernel or metrics differ", |
1509 | 0 | manager); |
1510 | 0 | return false; |
1511 | 0 | } |
1512 | 0 | if (existing->nexthop.weight != requesting->nexthop.weight) { |
1513 | 0 | log_debug("Cannot update route: existing weight: %u, requesting weight: %u", |
1514 | 0 | existing->nexthop.weight, requesting->nexthop.weight); |
1515 | 0 | return false; |
1516 | 0 | } |
1517 | 0 | return true; |
1518 | | |
1519 | 0 | default: |
1520 | 0 | assert_not_reached(); |
1521 | 0 | } |
1522 | 0 | } |
1523 | | |
1524 | 0 | static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) { |
1525 | 0 | _cleanup_(route_unrefp) Route *tmp = NULL; |
1526 | 0 | Route *existing; |
1527 | 0 | int r; |
1528 | |
|
1529 | 0 | assert(link); |
1530 | 0 | assert(route); |
1531 | |
|
1532 | 0 | r = route_dup(route, nh, &tmp); |
1533 | 0 | if (r < 0) |
1534 | 0 | return r; |
1535 | | |
1536 | 0 | r = route_adjust_nexthops(tmp, link); |
1537 | 0 | if (r < 0) |
1538 | 0 | return r; |
1539 | | |
1540 | 0 | if (route_get(link->manager, tmp, &existing) < 0) |
1541 | 0 | return 0; |
1542 | | |
1543 | 0 | if (!route_can_update(link->manager, existing, tmp)) |
1544 | 0 | return 0; |
1545 | | |
1546 | 0 | route_unmark(existing); |
1547 | 0 | return 1; |
1548 | 0 | } |
1549 | | |
1550 | 0 | int link_drop_routes(Link *link, bool only_static) { |
1551 | 0 | Route *route; |
1552 | 0 | Link *other; |
1553 | 0 | int r = 0; |
1554 | |
|
1555 | 0 | assert(link); |
1556 | 0 | assert(link->manager); |
1557 | | |
1558 | | /* First, mark all routes. */ |
1559 | 0 | SET_FOREACH(route, link->manager->routes) { |
1560 | | /* Do not touch routes managed by the kernel. */ |
1561 | 0 | if (route_by_kernel(route)) |
1562 | 0 | continue; |
1563 | | |
1564 | | /* Ignore routes not assigned yet or already removed. */ |
1565 | 0 | if (!route_exists(route)) |
1566 | 0 | continue; |
1567 | | |
1568 | 0 | if (!link_should_mark_config(link, only_static, route->source, route->protocol)) |
1569 | 0 | continue; |
1570 | | |
1571 | 0 | Link *route_link = NULL; |
1572 | 0 | if (route_get_link(link->manager, route, &route_link) >= 0 && route_link != link) { |
1573 | | /* When we also mark foreign routes, do not mark routes assigned to other interfaces. |
1574 | | * Otherwise, routes assigned to unmanaged interfaces will be dropped. |
1575 | | * Note, route_get_link() does not provide assigned link for routes with an |
1576 | | * unreachable type or IPv4 multipath routes. So, the current implementation does not |
1577 | | * support managing such routes by other daemon or so, unless ManageForeignRoutes=no. */ |
1578 | 0 | if (!only_static) |
1579 | 0 | continue; |
1580 | | |
1581 | | /* When we mark only static routes, do not mark routes assigned to links that we do |
1582 | | * not know the assignment of .network files to the interfaces. Otherwise, if an |
1583 | | * interface is in the pending state, even if the .network file to be assigned to the |
1584 | | * interface has KeepConfiguration=yes, routes on the interface will be removed. |
1585 | | * This is especially important when systemd-networkd is restarted. */ |
1586 | 0 | if (!IN_SET(route_link->state, LINK_STATE_UNMANAGED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) |
1587 | 0 | continue; |
1588 | 0 | } |
1589 | | |
1590 | 0 | route_mark(route); |
1591 | 0 | } |
1592 | | |
1593 | | /* Then, unmark all routes requested by active links. */ |
1594 | 0 | HASHMAP_FOREACH(other, link->manager->links_by_index) { |
1595 | 0 | if (only_static && other == link) |
1596 | 0 | continue; |
1597 | | |
1598 | 0 | if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) |
1599 | 0 | continue; |
1600 | | |
1601 | 0 | HASHMAP_FOREACH(route, other->network->routes_by_section) { |
1602 | 0 | if (route->source != NETWORK_CONFIG_SOURCE_STATIC) |
1603 | 0 | continue; |
1604 | | |
1605 | 0 | if (route->family == AF_INET || ordered_set_isempty(route->nexthops)) { |
1606 | 0 | r = link_unmark_route(other, route, NULL); |
1607 | 0 | if (r < 0) |
1608 | 0 | return r; |
1609 | |
|
1610 | 0 | } else { |
1611 | 0 | RouteNextHop *nh; |
1612 | 0 | ORDERED_SET_FOREACH(nh, route->nexthops) { |
1613 | 0 | r = link_unmark_route(other, route, nh); |
1614 | 0 | if (r < 0) |
1615 | 0 | return r; |
1616 | 0 | } |
1617 | 0 | } |
1618 | 0 | } |
1619 | | |
1620 | | /* Also unmark routes requested in .netdev file. */ |
1621 | 0 | if (other->netdev && other->netdev->kind == NETDEV_KIND_WIREGUARD) { |
1622 | 0 | Wireguard *w = WIREGUARD(other->netdev); |
1623 | |
|
1624 | 0 | SET_FOREACH(route, w->routes) { |
1625 | 0 | r = link_unmark_route(other, route, NULL); |
1626 | 0 | if (r < 0) |
1627 | 0 | return r; |
1628 | 0 | } |
1629 | 0 | } |
1630 | 0 | } |
1631 | | |
1632 | | /* Finally, remove all marked routes. */ |
1633 | 0 | SET_FOREACH(route, link->manager->routes) { |
1634 | 0 | if (!route_is_marked(route)) |
1635 | 0 | continue; |
1636 | | |
1637 | 0 | RET_GATHER(r, route_remove(route, link->manager)); |
1638 | 0 | } |
1639 | |
|
1640 | 0 | return r; |
1641 | 0 | } |
1642 | | |
1643 | 0 | void link_forget_routes(Link *link) { |
1644 | 0 | assert(link); |
1645 | 0 | assert(link->ifindex > 0); |
1646 | 0 | assert(!link_is_up(link)); |
1647 | | |
1648 | | /* When an interface went down, IPv4 non-local routes bound to the interface are silently removed by |
1649 | | * the kernel, without any notifications. Let's forget them in that case. Otherwise, when the link |
1650 | | * goes up later, the configuration order of routes may be confused by the nonexistent routes. |
1651 | | * See issue #35047. */ |
1652 | |
|
1653 | 0 | Route *route; |
1654 | 0 | SET_FOREACH(route, link->manager->routes) { |
1655 | | // TODO: handle multipath routes |
1656 | 0 | if (route->nexthop.ifindex != link->ifindex) |
1657 | 0 | continue; |
1658 | 0 | if (route->family != AF_INET) |
1659 | 0 | continue; |
1660 | | // TODO: check RTN_NAT and RTN_XRESOLVE |
1661 | 0 | if (!IN_SET(route->type, RTN_UNICAST, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST)) |
1662 | 0 | continue; |
1663 | | |
1664 | 0 | route_forget(link->manager, route, "Forgetting silently removed"); |
1665 | 0 | } |
1666 | 0 | } |
1667 | | |
1668 | 24.4k | int network_add_ipv4ll_route(Network *network) { |
1669 | 24.4k | _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; |
1670 | 24.4k | unsigned section_line; |
1671 | 24.4k | int r; |
1672 | | |
1673 | 24.4k | assert(network); |
1674 | | |
1675 | 24.4k | if (!network->ipv4ll_route) |
1676 | 24.4k | return 0; |
1677 | | |
1678 | 29 | r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, §ion_line); |
1679 | 29 | if (r < 0) |
1680 | 0 | return r; |
1681 | | |
1682 | | /* IPv4LLRoute= is in [Network] section. */ |
1683 | 29 | r = route_new_static(network, network->filename, section_line, &route); |
1684 | 29 | if (r < 0) |
1685 | 0 | return r; |
1686 | | |
1687 | 29 | r = in_addr_from_string(AF_INET, "169.254.0.0", &route->dst); |
1688 | 29 | if (r < 0) |
1689 | 0 | return r; |
1690 | | |
1691 | 29 | route->family = AF_INET; |
1692 | 29 | route->dst_prefixlen = 16; |
1693 | 29 | route->scope = RT_SCOPE_LINK; |
1694 | 29 | route->scope_set = true; |
1695 | 29 | route->table_set = true; |
1696 | 29 | route->priority = IPV4LL_ROUTE_METRIC; |
1697 | 29 | route->protocol = RTPROT_STATIC; |
1698 | | |
1699 | 29 | TAKE_PTR(route); |
1700 | 29 | return 0; |
1701 | 29 | } |
1702 | | |
1703 | 24.4k | int network_add_default_route_on_device(Network *network) { |
1704 | 24.4k | _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; |
1705 | 24.4k | unsigned section_line; |
1706 | 24.4k | int r; |
1707 | | |
1708 | 24.4k | assert(network); |
1709 | | |
1710 | 24.4k | if (!network->default_route_on_device) |
1711 | 24.4k | return 0; |
1712 | | |
1713 | 19 | r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, §ion_line); |
1714 | 19 | if (r < 0) |
1715 | 0 | return r; |
1716 | | |
1717 | | /* DefaultRouteOnDevice= is in [Network] section. */ |
1718 | 19 | r = route_new_static(network, network->filename, section_line, &route); |
1719 | 19 | if (r < 0) |
1720 | 0 | return r; |
1721 | | |
1722 | 19 | route->family = AF_INET; |
1723 | 19 | route->scope = RT_SCOPE_LINK; |
1724 | 19 | route->scope_set = true; |
1725 | 19 | route->protocol = RTPROT_STATIC; |
1726 | | |
1727 | 19 | TAKE_PTR(route); |
1728 | 19 | return 0; |
1729 | 19 | } |
1730 | | |
1731 | | static int config_parse_preferred_src( |
1732 | | const char *unit, |
1733 | | const char *filename, |
1734 | | unsigned line, |
1735 | | const char *section, |
1736 | | unsigned section_line, |
1737 | | const char *lvalue, |
1738 | | int ltype, |
1739 | | const char *rvalue, |
1740 | | void *data, |
1741 | 971 | void *userdata) { |
1742 | | |
1743 | 971 | Route *route = ASSERT_PTR(userdata); |
1744 | 971 | int r; |
1745 | | |
1746 | 971 | if (isempty(rvalue)) { |
1747 | 194 | route->prefsrc_set = false; |
1748 | 194 | route->prefsrc = IN_ADDR_NULL; |
1749 | 194 | return 1; |
1750 | 194 | } |
1751 | | |
1752 | 777 | r = parse_boolean(rvalue); |
1753 | 777 | if (r == 0) { |
1754 | | /* Accepts only no. That prohibits prefsrc set by DHCP lease. */ |
1755 | 194 | route->prefsrc_set = true; |
1756 | 194 | route->prefsrc = IN_ADDR_NULL; |
1757 | 194 | return 1; |
1758 | 194 | } |
1759 | | |
1760 | 583 | if (route->family == AF_UNSPEC) |
1761 | 203 | r = in_addr_from_string_auto(rvalue, &route->family, &route->prefsrc); |
1762 | 380 | else |
1763 | 380 | r = in_addr_from_string(route->family, rvalue, &route->prefsrc); |
1764 | 583 | if (r < 0) |
1765 | 389 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1766 | | |
1767 | 194 | route->prefsrc_set = true; |
1768 | 194 | return 1; |
1769 | 583 | } |
1770 | | |
1771 | | static int config_parse_route_destination( |
1772 | | const char *unit, |
1773 | | const char *filename, |
1774 | | unsigned line, |
1775 | | const char *section, |
1776 | | unsigned section_line, |
1777 | | const char *lvalue, |
1778 | | int ltype, |
1779 | | const char *rvalue, |
1780 | | void *data, |
1781 | 3.19k | void *userdata) { |
1782 | | |
1783 | 3.19k | Route *route = ASSERT_PTR(userdata); |
1784 | 3.19k | union in_addr_union *buffer; |
1785 | 3.19k | unsigned char *prefixlen; |
1786 | 3.19k | int r; |
1787 | | |
1788 | 3.19k | assert(lvalue); |
1789 | 3.19k | assert(rvalue); |
1790 | | |
1791 | 3.19k | if (streq(lvalue, "Destination")) { |
1792 | 1.00k | buffer = &route->dst; |
1793 | 1.00k | prefixlen = &route->dst_prefixlen; |
1794 | 2.19k | } else if (streq(lvalue, "Source")) { |
1795 | 2.19k | buffer = &route->src; |
1796 | 2.19k | prefixlen = &route->src_prefixlen; |
1797 | 2.19k | } else |
1798 | 0 | assert_not_reached(); |
1799 | | |
1800 | 3.19k | if (route->family == AF_UNSPEC) |
1801 | 2.80k | r = in_addr_prefix_from_string_auto(rvalue, &route->family, buffer, prefixlen); |
1802 | 388 | else |
1803 | 388 | r = in_addr_prefix_from_string(rvalue, route->family, buffer, prefixlen); |
1804 | 3.19k | if (r < 0) |
1805 | 650 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1806 | | |
1807 | 2.54k | (void) in_addr_mask(route->family, buffer, *prefixlen); |
1808 | 2.54k | return 1; |
1809 | 3.19k | } |
1810 | | |
1811 | | static int config_parse_route_priority( |
1812 | | const char *unit, |
1813 | | const char *filename, |
1814 | | unsigned line, |
1815 | | const char *section, |
1816 | | unsigned section_line, |
1817 | | const char *lvalue, |
1818 | | int ltype, |
1819 | | const char *rvalue, |
1820 | | void *data, |
1821 | 597 | void *userdata) { |
1822 | | |
1823 | 597 | Route *route = ASSERT_PTR(userdata); |
1824 | 597 | int r; |
1825 | | |
1826 | 597 | assert(rvalue); |
1827 | | |
1828 | 597 | r = safe_atou32(rvalue, &route->priority); |
1829 | 597 | if (r < 0) |
1830 | 197 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1831 | | |
1832 | 400 | route->priority_set = true; |
1833 | 400 | return 1; |
1834 | 597 | } |
1835 | | |
1836 | | static int config_parse_route_scope( |
1837 | | const char *unit, |
1838 | | const char *filename, |
1839 | | unsigned line, |
1840 | | const char *section, |
1841 | | unsigned section_line, |
1842 | | const char *lvalue, |
1843 | | int ltype, |
1844 | | const char *rvalue, |
1845 | | void *data, |
1846 | 912 | void *userdata) { |
1847 | | |
1848 | 912 | Route *route = ASSERT_PTR(userdata); |
1849 | 912 | int r; |
1850 | | |
1851 | 912 | assert(rvalue); |
1852 | | |
1853 | 912 | r = route_scope_from_string(rvalue); |
1854 | 912 | if (r < 0) |
1855 | 314 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1856 | | |
1857 | 598 | route->scope = r; |
1858 | 598 | route->scope_set = true; |
1859 | 598 | return 1; |
1860 | 912 | } |
1861 | | |
1862 | | static int config_parse_route_table( |
1863 | | const char *unit, |
1864 | | const char *filename, |
1865 | | unsigned line, |
1866 | | const char *section, |
1867 | | unsigned section_line, |
1868 | | const char *lvalue, |
1869 | | int ltype, |
1870 | | const char *rvalue, |
1871 | | void *data, |
1872 | 1.13k | void *userdata) { |
1873 | | |
1874 | 1.13k | Route *route = ASSERT_PTR(userdata); |
1875 | 2.27k | Manager *manager = ASSERT_PTR(ASSERT_PTR(route->network)->manager); |
1876 | 2.27k | int r; |
1877 | | |
1878 | 2.27k | assert(rvalue); |
1879 | | |
1880 | 2.27k | r = manager_get_route_table_from_string(manager, rvalue, &route->table); |
1881 | 1.13k | if (r < 0) |
1882 | 503 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1883 | | |
1884 | 634 | route->table_set = true; |
1885 | 634 | return 1; |
1886 | 1.13k | } |
1887 | | |
1888 | | static int config_parse_route_preference( |
1889 | | const char *unit, |
1890 | | const char *filename, |
1891 | | unsigned line, |
1892 | | const char *section, |
1893 | | unsigned section_line, |
1894 | | const char *lvalue, |
1895 | | int ltype, |
1896 | | const char *rvalue, |
1897 | | void *data, |
1898 | 841 | void *userdata) { |
1899 | | |
1900 | 841 | Route *route = ASSERT_PTR(userdata); |
1901 | | |
1902 | 841 | assert(rvalue); |
1903 | | |
1904 | 841 | if (streq(rvalue, "low")) |
1905 | 194 | route->pref = SD_NDISC_PREFERENCE_LOW; |
1906 | 647 | else if (streq(rvalue, "medium")) |
1907 | 198 | route->pref = SD_NDISC_PREFERENCE_MEDIUM; |
1908 | 449 | else if (streq(rvalue, "high")) |
1909 | 196 | route->pref = SD_NDISC_PREFERENCE_HIGH; |
1910 | 253 | else |
1911 | 253 | return log_syntax_parse_error(unit, filename, line, 0, lvalue, rvalue); |
1912 | | |
1913 | 588 | route->pref_set = true; |
1914 | 588 | return 1; |
1915 | 841 | } |
1916 | | |
1917 | | static int config_parse_route_protocol( |
1918 | | const char *unit, |
1919 | | const char *filename, |
1920 | | unsigned line, |
1921 | | const char *section, |
1922 | | unsigned section_line, |
1923 | | const char *lvalue, |
1924 | | int ltype, |
1925 | | const char *rvalue, |
1926 | | void *data, |
1927 | 404 | void *userdata) { |
1928 | | |
1929 | 404 | unsigned char *p = ASSERT_PTR(data); |
1930 | 404 | int r; |
1931 | | |
1932 | 404 | assert(rvalue); |
1933 | | |
1934 | 404 | r = route_protocol_from_string(rvalue); |
1935 | 404 | if (r < 0) |
1936 | 205 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1937 | | |
1938 | 199 | *p = (unsigned char) r; |
1939 | 199 | return 1; |
1940 | 404 | } |
1941 | | |
1942 | | static int config_parse_route_type( |
1943 | | const char *unit, |
1944 | | const char *filename, |
1945 | | unsigned line, |
1946 | | const char *section, |
1947 | | unsigned section_line, |
1948 | | const char *lvalue, |
1949 | | int ltype, |
1950 | | const char *rvalue, |
1951 | | void *data, |
1952 | 2.76k | void *userdata) { |
1953 | | |
1954 | 2.76k | unsigned char *p = ASSERT_PTR(data); |
1955 | 2.76k | int r; |
1956 | | |
1957 | 2.76k | assert(rvalue); |
1958 | | |
1959 | 2.76k | r = route_type_from_string(rvalue); |
1960 | 2.76k | if (r < 0) |
1961 | 497 | return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue); |
1962 | | |
1963 | 2.26k | *p = (unsigned char) r; |
1964 | 2.26k | return 1; |
1965 | 2.76k | } |
1966 | | |
1967 | | int config_parse_route_section( |
1968 | | const char *unit, |
1969 | | const char *filename, |
1970 | | unsigned line, |
1971 | | const char *section, |
1972 | | unsigned section_line, |
1973 | | const char *lvalue, |
1974 | | int ltype, |
1975 | | const char *rvalue, |
1976 | | void *data, |
1977 | 59.7k | void *userdata) { |
1978 | | |
1979 | 59.7k | static const ConfigSectionParser table[_ROUTE_CONF_PARSER_MAX] = { |
1980 | 59.7k | [ROUTE_DESTINATION] = { .parser = config_parse_route_destination, .ltype = 0, .offset = 0, }, |
1981 | 59.7k | [ROUTE_PREFERRED_SOURCE] = { .parser = config_parse_preferred_src, .ltype = 0, .offset = 0, }, |
1982 | 59.7k | [ROUTE_PRIORITY] = { .parser = config_parse_route_priority, .ltype = 0, .offset = 0, }, |
1983 | 59.7k | [ROUTE_SCOPE] = { .parser = config_parse_route_scope, .ltype = 0, .offset = 0, }, |
1984 | 59.7k | [ROUTE_TABLE] = { .parser = config_parse_route_table, .ltype = 0, .offset = 0, }, |
1985 | 59.7k | [ROUTE_PREFERENCE] = { .parser = config_parse_route_preference, .ltype = 0, .offset = 0, }, |
1986 | 59.7k | [ROUTE_PROTOCOL] = { .parser = config_parse_route_protocol, .ltype = 0, .offset = offsetof(Route, protocol), }, |
1987 | 59.7k | [ROUTE_TYPE] = { .parser = config_parse_route_type, .ltype = 0, .offset = offsetof(Route, type), }, |
1988 | 59.7k | [ROUTE_GATEWAY_NETWORK] = { .parser = config_parse_gateway, .ltype = 0, .offset = 0, }, |
1989 | 59.7k | [ROUTE_GATEWAY] = { .parser = config_parse_gateway, .ltype = 1, .offset = 0, }, |
1990 | 59.7k | [ROUTE_GATEWAY_ONLINK] = { .parser = config_parse_tristate, .ltype = 0, .offset = offsetof(Route, gateway_onlink), }, |
1991 | 59.7k | [ROUTE_MULTIPATH] = { .parser = config_parse_multipath_route, .ltype = 0, .offset = offsetof(Route, nexthops), }, |
1992 | 59.7k | [ROUTE_NEXTHOP] = { .parser = config_parse_route_nexthop, .ltype = 0, .offset = offsetof(Route, nexthop_id), }, |
1993 | 59.7k | [ROUTE_METRIC_MTU] = { .parser = config_parse_route_metric, .ltype = RTAX_MTU, .offset = 0, }, |
1994 | 59.7k | [ROUTE_METRIC_ADVMSS] = { .parser = config_parse_route_metric, .ltype = RTAX_ADVMSS, .offset = 0, }, |
1995 | 59.7k | [ROUTE_METRIC_HOPLIMIT] = { .parser = config_parse_route_metric, .ltype = RTAX_HOPLIMIT, .offset = 0, }, |
1996 | 59.7k | [ROUTE_METRIC_INITCWND] = { .parser = config_parse_route_metric, .ltype = RTAX_INITCWND, .offset = 0, }, |
1997 | 59.7k | [ROUTE_METRIC_RTO_MIN] = { .parser = config_parse_route_metric, .ltype = RTAX_RTO_MIN, .offset = 0, }, |
1998 | 59.7k | [ROUTE_METRIC_INITRWND] = { .parser = config_parse_route_metric, .ltype = RTAX_INITRWND, .offset = 0, }, |
1999 | 59.7k | [ROUTE_METRIC_QUICKACK] = { .parser = config_parse_route_metric, .ltype = RTAX_QUICKACK, .offset = 0, }, |
2000 | 59.7k | [ROUTE_METRIC_CC_ALGO] = { .parser = config_parse_string, .ltype = 0, .offset = offsetof(Route, metric.tcp_congestion_control_algo), }, |
2001 | 59.7k | [ROUTE_METRIC_FASTOPEN_NO_COOKIE] = { .parser = config_parse_route_metric, .ltype = RTAX_FASTOPEN_NO_COOKIE, .offset = 0, }, |
2002 | 59.7k | }; |
2003 | | |
2004 | 59.7k | _cleanup_(route_unref_or_set_invalidp) Route *route = NULL; |
2005 | 59.7k | Network *network = ASSERT_PTR(userdata); |
2006 | 59.7k | int r; |
2007 | | |
2008 | 59.7k | assert(filename); |
2009 | | |
2010 | 59.7k | if (streq(section, "Network")) { |
2011 | 26.3k | assert(streq_ptr(lvalue, "Gateway")); |
2012 | | |
2013 | | /* Clear all previously defined routes when Gateway= (empty) is set in [Network] section */ |
2014 | 26.3k | if (isempty(rvalue)) { |
2015 | 200 | network->routes_by_section = hashmap_free(network->routes_by_section); |
2016 | 200 | return 0; |
2017 | 200 | } |
2018 | | |
2019 | | /* we are not in an Route section, so use line number instead */ |
2020 | 26.1k | r = route_new_static(network, filename, line, &route); |
2021 | 26.1k | } else |
2022 | 33.3k | r = route_new_static(network, filename, section_line, &route); |
2023 | 59.5k | if (r == -ENOMEM) |
2024 | 0 | return log_oom(); |
2025 | 59.5k | if (r < 0) { |
2026 | 0 | log_syntax(unit, LOG_WARNING, filename, line, r, |
2027 | 0 | "Failed to allocate route, ignoring assignment: %m"); |
2028 | 0 | return 0; |
2029 | 0 | } |
2030 | | |
2031 | 59.5k | r = config_section_parse(table, ELEMENTSOF(table), |
2032 | 59.5k | unit, filename, line, section, section_line, lvalue, ltype, rvalue, route); |
2033 | 59.5k | if (r <= 0) |
2034 | 34.6k | return r; |
2035 | | |
2036 | 24.9k | TAKE_PTR(route); |
2037 | 24.9k | return 0; |
2038 | 59.5k | } |
2039 | | |
2040 | 40.4k | int route_section_verify(Route *route) { |
2041 | 40.4k | int r; |
2042 | | |
2043 | 40.4k | assert(route); |
2044 | 40.4k | assert(route->section); |
2045 | | |
2046 | 40.4k | if (section_is_invalid(route->section)) |
2047 | 14.9k | return -EINVAL; |
2048 | | |
2049 | | /* Currently, we do not support static route with finite lifetime. */ |
2050 | 40.4k | assert(route->lifetime_usec == USEC_INFINITY); |
2051 | | |
2052 | 25.4k | r = route_section_verify_nexthops(route); |
2053 | 25.4k | if (r < 0) |
2054 | 2.95k | return r; |
2055 | | |
2056 | | /* table */ |
2057 | 22.5k | if (!route->table_set && route->network && route->network->vrf) { |
2058 | 0 | route->table = VRF(route->network->vrf)->table; |
2059 | 0 | route->table_set = true; |
2060 | 0 | } |
2061 | | |
2062 | 22.5k | if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT)) |
2063 | 516 | route->table = RT_TABLE_LOCAL; |
2064 | | |
2065 | | /* scope */ |
2066 | 22.5k | if (!route->scope_set && route->family == AF_INET) { |
2067 | 9.70k | if (IN_SET(route->type, RTN_LOCAL, RTN_NAT)) |
2068 | 322 | route->scope = RT_SCOPE_HOST; |
2069 | 9.38k | else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST)) |
2070 | 194 | route->scope = RT_SCOPE_LINK; |
2071 | 9.18k | else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) && |
2072 | 8.99k | !route->gateway_from_dhcp_or_ra && |
2073 | 8.79k | !in_addr_is_set(route->nexthop.family, &route->nexthop.gw) && |
2074 | 8.38k | ordered_set_isempty(route->nexthops) && |
2075 | 8.18k | route->nexthop_id == 0) |
2076 | 7.94k | route->scope = RT_SCOPE_LINK; |
2077 | 9.70k | } |
2078 | | |
2079 | | /* IPv6 route */ |
2080 | 22.5k | if (route->family == AF_INET6) { |
2081 | 12.7k | if (route->scope != RT_SCOPE_UNIVERSE) { |
2082 | 196 | log_section_warning(route->section, "Scope= is specified for IPv6 route. It will be ignored."); |
2083 | 196 | route->scope = RT_SCOPE_UNIVERSE; |
2084 | 196 | } |
2085 | | |
2086 | 12.7k | if (route->priority == 0) |
2087 | 10.4k | route->priority = IP6_RT_PRIO_USER; |
2088 | 12.7k | } |
2089 | | |
2090 | 22.5k | return 0; |
2091 | 22.5k | } |
2092 | | |
2093 | 3.22k | void network_drop_invalid_routes(Network *network) { |
2094 | 3.22k | Route *route; |
2095 | | |
2096 | 3.22k | assert(network); |
2097 | | |
2098 | 3.22k | HASHMAP_FOREACH(route, network->routes_by_section) |
2099 | 22.9k | if (route_section_verify(route) < 0) |
2100 | 17.9k | route_detach(route); |
2101 | 3.22k | } |