]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / network / networkd-route.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
f579559b 2
b5bf6f64 3#include <linux/icmpv6.h>
8973df5c 4#include <linux/ipv6_route.h>
228c3e21 5#include <linux/nexthop.h>
b5bf6f64 6
b5efdb8a 7#include "alloc-util.h"
0b0c81bb 8#include "event-util.h"
fc2f9534 9#include "netlink-util.h"
3b6a3bde 10#include "networkd-address.h"
ca5ad760 11#include "networkd-ipv4ll.h"
23f53b99 12#include "networkd-manager.h"
e2263711 13#include "networkd-network.h"
141318f7 14#include "networkd-nexthop.h"
76c5a0f2 15#include "networkd-queue.h"
344b3cff 16#include "networkd-route-util.h"
6bedfcbb 17#include "networkd-route.h"
6bedfcbb 18#include "parse-util.h"
07630cea 19#include "string-util.h"
d96edb2c 20#include "strv.h"
c0d48bc5 21#include "vrf.h"
e9084344 22#include "wireguard.h"
f579559b 23
ed9e361a 24int route_new(Route **ret) {
8e766630 25 _cleanup_(route_freep) Route *route = NULL;
f0213e37 26
17f9c355 27 route = new(Route, 1);
f0213e37
TG
28 if (!route)
29 return -ENOMEM;
30
17f9c355
YW
31 *route = (Route) {
32 .family = AF_UNSPEC,
33 .scope = RT_SCOPE_UNIVERSE,
34 .protocol = RTPROT_UNSPEC,
35 .type = RTN_UNICAST,
36 .table = RT_TABLE_MAIN,
91fc5135 37 .lifetime_usec = USEC_INFINITY,
17f9c355 38 .quickack = -1,
633c7258 39 .fast_open_no_cookie = -1,
54901fd2 40 .gateway_onlink = -1,
9b88f20a 41 .ttl_propagate = -1,
17f9c355 42 };
f0213e37 43
1cc6c93a 44 *ret = TAKE_PTR(route);
f0213e37
TG
45
46 return 0;
47}
48
9560e5b3 49static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
307fe3cd 50 _cleanup_(config_section_freep) ConfigSection *n = NULL;
8e766630 51 _cleanup_(route_freep) Route *route = NULL;
f0213e37 52 int r;
f579559b 53
8c34b963
LP
54 assert(network);
55 assert(ret);
2a54a044
YW
56 assert(filename);
57 assert(section_line > 0);
f4859fc7 58
307fe3cd 59 r = config_section_new(filename, section_line, &n);
2a54a044
YW
60 if (r < 0)
61 return r;
6ae115c1 62
2a54a044
YW
63 route = hashmap_get(network->routes_by_section, n);
64 if (route) {
65 *ret = TAKE_PTR(route);
66 return 0;
6ae115c1
TG
67 }
68
2a54a044 69 if (hashmap_size(network->routes_by_section) >= routes_max())
8c34b963
LP
70 return -E2BIG;
71
ed9e361a 72 r = route_new(&route);
f0213e37
TG
73 if (r < 0)
74 return r;
801bd9e8 75
ed9e361a 76 route->protocol = RTPROT_STATIC;
0f7f2769 77 route->network = network;
2a54a044 78 route->section = TAKE_PTR(n);
3b6a3bde 79 route->source = NETWORK_CONFIG_SOURCE_STATIC;
cacc1dbf 80
307fe3cd 81 r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
2a54a044
YW
82 if (r < 0)
83 return r;
6ae115c1 84
1cc6c93a 85 *ret = TAKE_PTR(route);
f579559b
TG
86 return 0;
87}
88
169948e9 89Route *route_free(Route *route) {
f579559b 90 if (!route)
169948e9 91 return NULL;
f579559b 92
f048a16b 93 if (route->network) {
2a54a044
YW
94 assert(route->section);
95 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 96 }
6ae115c1 97
307fe3cd 98 config_section_free(route->section);
fd45e522 99
3b6a3bde 100 if (route->link)
1c8e710c 101 set_remove(route->link->routes, route);
3b6a3bde
YW
102
103 if (route->manager)
ad208fac 104 set_remove(route->manager->routes, route);
ad208fac 105
e8f52f3c 106 ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
6ff5cc6b 107
d105befc 108 sd_event_source_disable_unref(route->expire);
f833694d 109
dc7c21f0
YW
110 free(route->tcp_congestion_control_algo);
111
169948e9 112 return mfree(route);
f579559b
TG
113}
114
09d09207 115static void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
116 assert(route);
117
118 siphash24_compress(&route->family, sizeof(route->family), state);
119
01aaa3df
YW
120 switch (route->family) {
121 case AF_INET:
122 case AF_INET6:
01aaa3df 123 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
67e05dd8
ZJS
124 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
125
01aaa3df 126 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
67e05dd8
ZJS
127 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
128
6dd53981 129 siphash24_compress(&route->gw_family, sizeof(route->gw_family), state);
40075951 130 if (IN_SET(route->gw_family, AF_INET, AF_INET6)) {
6dd53981 131 siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->gw_family), state);
40075951
YW
132 siphash24_compress(&route->gw_weight, sizeof(route->gw_weight), state);
133 }
67e05dd8 134
01aaa3df
YW
135 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
136
137 siphash24_compress(&route->tos, sizeof(route->tos), state);
138 siphash24_compress(&route->priority, sizeof(route->priority), state);
139 siphash24_compress(&route->table, sizeof(route->table), state);
140 siphash24_compress(&route->protocol, sizeof(route->protocol), state);
141 siphash24_compress(&route->scope, sizeof(route->scope), state);
142 siphash24_compress(&route->type, sizeof(route->type), state);
67e05dd8 143
fa3e401a
YW
144 siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
145 siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
01aaa3df 146
007cac09 147 siphash24_compress(&route->advmss, sizeof(route->advmss), state);
324e3422 148 siphash24_compress(&route->nexthop_id, sizeof(route->nexthop_id), state);
007cac09 149
01aaa3df
YW
150 break;
151 default:
152 /* treat any other address family as AF_UNSPEC */
153 break;
154 }
155}
156
09d09207 157static int route_compare_func(const Route *a, const Route *b) {
01aaa3df
YW
158 int r;
159
160 r = CMP(a->family, b->family);
161 if (r != 0)
162 return r;
163
164 switch (a->family) {
165 case AF_INET:
166 case AF_INET6:
167 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
168 if (r != 0)
169 return r;
170
67e05dd8
ZJS
171 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
172 if (r != 0)
173 return r;
174
01aaa3df
YW
175 r = CMP(a->src_prefixlen, b->src_prefixlen);
176 if (r != 0)
177 return r;
178
67e05dd8 179 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
180 if (r != 0)
181 return r;
182
6dd53981 183 r = CMP(a->gw_family, b->gw_family);
01aaa3df
YW
184 if (r != 0)
185 return r;
186
6dd53981
YW
187 if (IN_SET(a->gw_family, AF_INET, AF_INET6)) {
188 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
189 if (r != 0)
190 return r;
40075951
YW
191
192 r = CMP(a->gw_weight, b->gw_weight);
193 if (r != 0)
194 return r;
6dd53981
YW
195 }
196
67e05dd8 197 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
198 if (r != 0)
199 return r;
200
67e05dd8 201 r = CMP(a->tos, b->tos);
01aaa3df
YW
202 if (r != 0)
203 return r;
204
67e05dd8 205 r = CMP(a->priority, b->priority);
01aaa3df
YW
206 if (r != 0)
207 return r;
208
67e05dd8 209 r = CMP(a->table, b->table);
01aaa3df
YW
210 if (r != 0)
211 return r;
212
67e05dd8 213 r = CMP(a->protocol, b->protocol);
fa3e401a
YW
214 if (r != 0)
215 return r;
216
67e05dd8 217 r = CMP(a->scope, b->scope);
fa3e401a
YW
218 if (r != 0)
219 return r;
220
67e05dd8 221 r = CMP(a->type, b->type);
01aaa3df
YW
222 if (r != 0)
223 return r;
224
67e05dd8 225 r = CMP(a->initcwnd, b->initcwnd);
01aaa3df
YW
226 if (r != 0)
227 return r;
228
67e05dd8 229 r = CMP(a->initrwnd, b->initrwnd);
01aaa3df
YW
230 if (r != 0)
231 return r;
232
007cac09
SS
233 r = CMP(a->advmss, b->advmss);
234 if (r != 0)
235 return r;
236
324e3422
YW
237 r = CMP(a->nexthop_id, b->nexthop_id);
238 if (r != 0)
239 return r;
240
67e05dd8 241 return 0;
01aaa3df
YW
242 default:
243 /* treat any other address family as AF_UNSPEC */
244 return 0;
245 }
246}
247
28870a9d 248DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
c077a205 249 route_hash_ops,
01aaa3df 250 Route,
c077a205
YW
251 route_hash_func,
252 route_compare_func,
01aaa3df
YW
253 route_free);
254
3b6a3bde
YW
255static bool route_type_is_reject(const Route *route) {
256 assert(route);
7ecf0c3e 257
3b6a3bde
YW
258 return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
259}
260
261static bool route_needs_convert(const Route *route) {
262 assert(route);
7ecf0c3e 263
3b6a3bde 264 return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes);
7ecf0c3e
TJ
265}
266
3b6a3bde
YW
267static int route_add(Manager *manager, Link *link, Route *route) {
268 int r;
1b566071 269
3b6a3bde 270 assert(route);
1c8e710c 271
3b6a3bde
YW
272 if (route_type_is_reject(route)) {
273 assert(manager);
ad208fac 274
3b6a3bde
YW
275 r = set_ensure_put(&manager->routes, &route_hash_ops, route);
276 if (r < 0)
277 return r;
278 if (r == 0)
279 return -EEXIST;
280
281 route->manager = manager;
282 } else {
283 assert(link);
284
285 r = set_ensure_put(&link->routes, &route_hash_ops, route);
286 if (r < 0)
287 return r;
288 if (r == 0)
289 return -EEXIST;
290
291 route->link = link;
1b566071 292 }
1c8e710c 293
3b6a3bde 294 return 0;
1c8e710c
TG
295}
296
3b6a3bde
YW
297int route_get(Manager *manager, Link *link, const Route *in, Route **ret) {
298 Route *route;
f9bb3338 299
3b6a3bde
YW
300 assert(in);
301
302 if (route_type_is_reject(in)) {
303 if (!manager)
304 return -ENOENT;
305
306 route = set_get(manager->routes, in);
f9bb3338 307 } else {
3b6a3bde
YW
308 if (!link)
309 return -ENOENT;
310
311 route = set_get(link->routes, in);
f9bb3338 312 }
3b6a3bde
YW
313 if (!route)
314 return -ENOENT;
315
316 if (ret)
317 *ret = route;
318
319 return 0;
f9bb3338 320}
889b550f 321
c0bd9eb1
YW
322int route_dup(const Route *src, Route **ret) {
323 _cleanup_(route_freep) Route *dest = NULL;
dc7c21f0 324 int r;
3b6a3bde
YW
325
326 /* This does not copy mulipath routes. */
c0bd9eb1
YW
327
328 assert(src);
329 assert(ret);
330
331 dest = newdup(Route, src, 1);
332 if (!dest)
333 return -ENOMEM;
334
335 /* Unset all pointers */
336 dest->network = NULL;
337 dest->section = NULL;
338 dest->link = NULL;
339 dest->manager = NULL;
340 dest->multipath_routes = NULL;
341 dest->expire = NULL;
dc7c21f0
YW
342 dest->tcp_congestion_control_algo = NULL;
343
344 r = free_and_strdup(&dest->tcp_congestion_control_algo, src->tcp_congestion_control_algo);
345 if (r < 0)
346 return r;
c0bd9eb1 347
3b6a3bde
YW
348 *ret = TAKE_PTR(dest);
349 return 0;
350}
c0bd9eb1 351
3b6a3bde
YW
352static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weight) {
353 assert(route);
354 assert(nh);
355 assert(hashmap_isempty(nh->group));
c0bd9eb1 356
3b6a3bde
YW
357 route->gw_family = nh->family;
358 route->gw = nh->gw;
c0bd9eb1 359
3b6a3bde
YW
360 if (nh_weight != UINT8_MAX)
361 route->gw_weight = nh_weight;
c0bd9eb1 362
3b6a3bde
YW
363 if (nh->blackhole)
364 route->type = RTN_BLACKHOLE;
c0bd9eb1
YW
365}
366
3b6a3bde
YW
367static void route_apply_multipath_route(Route *route, const MultipathRoute *m) {
368 assert(route);
369 assert(m);
1c8e710c 370
3b6a3bde
YW
371 route->gw_family = m->gateway.family;
372 route->gw = m->gateway.address;
373 route->gw_weight = m->weight;
374}
1c8e710c 375
3b6a3bde
YW
376static int multipath_route_get_link(Manager *manager, const MultipathRoute *m, Link **ret) {
377 int r;
1c8e710c 378
3b6a3bde
YW
379 assert(manager);
380 assert(m);
1c8e710c 381
3b6a3bde
YW
382 if (m->ifname) {
383 r = link_get_by_name(manager, m->ifname, ret);
384 return r < 0 ? r : 1;
1c8e710c 385
3b6a3bde
YW
386 } else if (m->ifindex > 0) { /* Always ignore ifindex if ifname is set. */
387 r = link_get_by_index(manager, m->ifindex, ret);
388 return r < 0 ? r : 1;
389 }
1c8e710c
TG
390
391 if (ret)
3b6a3bde 392 *ret = NULL;
1c8e710c
TG
393 return 0;
394}
395
3b6a3bde
YW
396typedef struct ConvertedRoutes {
397 size_t n;
398 Route **routes;
399 Link **links;
400} ConvertedRoutes;
1c8e710c 401
3b6a3bde
YW
402static ConvertedRoutes *converted_routes_free(ConvertedRoutes *c) {
403 if (!c)
404 return NULL;
1c8e710c 405
3b6a3bde
YW
406 for (size_t i = 0; i < c->n; i++)
407 route_free(c->routes[i]);
ad208fac 408
3b6a3bde
YW
409 free(c->routes);
410 free(c->links);
228c3e21 411
3b6a3bde
YW
412 return mfree(c);
413}
324e3422 414
3b6a3bde 415DEFINE_TRIVIAL_CLEANUP_FUNC(ConvertedRoutes*, converted_routes_free);
cc17f75f 416
3b6a3bde
YW
417static int converted_routes_new(size_t n, ConvertedRoutes **ret) {
418 _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
419 _cleanup_free_ Route **routes = NULL;
420 _cleanup_free_ Link **links = NULL;
cc17f75f 421
3b6a3bde
YW
422 assert(n > 0);
423 assert(ret);
cc17f75f 424
3b6a3bde
YW
425 routes = new0(Route*, n);
426 if (!routes)
427 return -ENOMEM;
ad208fac 428
3b6a3bde
YW
429 links = new0(Link*, n);
430 if (!links)
431 return -ENOMEM;
1c8e710c 432
3b6a3bde
YW
433 c = new(ConvertedRoutes, 1);
434 if (!c)
435 return -ENOMEM;
1c8e710c 436
3b6a3bde
YW
437 *c = (ConvertedRoutes) {
438 .n = n,
439 .routes = TAKE_PTR(routes),
440 .links = TAKE_PTR(links),
441 };
6c252588 442
3b6a3bde
YW
443 *ret = TAKE_PTR(c);
444 return 0;
6c252588
YW
445}
446
3b6a3bde
YW
447static int route_convert(Manager *manager, const Route *route, ConvertedRoutes **ret) {
448 _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
228c3e21
YW
449 int r;
450
3b6a3bde 451 assert(manager);
228c3e21 452 assert(route);
3b6a3bde 453 assert(ret);
228c3e21 454
3b6a3bde
YW
455 if (!route_needs_convert(route)) {
456 *ret = NULL;
457 return 0;
458 }
228c3e21 459
3b6a3bde
YW
460 if (route->nexthop_id > 0) {
461 struct nexthop_grp *nhg;
462 NextHop *nh;
228c3e21 463
3b6a3bde
YW
464 r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh);
465 if (r < 0)
466 return r;
228c3e21 467
3b6a3bde
YW
468 if (hashmap_isempty(nh->group)) {
469 r = converted_routes_new(1, &c);
470 if (r < 0)
471 return r;
884a63d7 472
3b6a3bde
YW
473 r = route_dup(route, &c->routes[0]);
474 if (r < 0)
475 return r;
884a63d7 476
3b6a3bde
YW
477 route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
478 c->links[0] = nh->link;
884a63d7 479
3b6a3bde
YW
480 *ret = TAKE_PTR(c);
481 return 1;
482 }
884a63d7 483
3b6a3bde
YW
484 r = converted_routes_new(hashmap_size(nh->group), &c);
485 if (r < 0)
486 return r;
884a63d7 487
3b6a3bde 488 size_t i = 0;
228c3e21
YW
489 HASHMAP_FOREACH(nhg, nh->group) {
490 NextHop *h;
884a63d7 491
3b6a3bde
YW
492 r = manager_get_nexthop_by_id(manager, nhg->id, &h);
493 if (r < 0)
494 return r;
228c3e21 495
3b6a3bde
YW
496 r = route_dup(route, &c->routes[i]);
497 if (r < 0)
228c3e21 498 return r;
3b6a3bde
YW
499
500 route_apply_nexthop(c->routes[i], h, nhg->weight);
501 c->links[i] = h->link;
502
503 i++;
228c3e21
YW
504 }
505
3b6a3bde
YW
506 *ret = TAKE_PTR(c);
507 return 1;
884a63d7 508
884a63d7
YW
509 }
510
3b6a3bde 511 assert(!ordered_set_isempty(route->multipath_routes));
884a63d7 512
3b6a3bde
YW
513 r = converted_routes_new(ordered_set_size(route->multipath_routes), &c);
514 if (r < 0)
515 return r;
884a63d7 516
3b6a3bde
YW
517 size_t i = 0;
518 MultipathRoute *m;
519 ORDERED_SET_FOREACH(m, route->multipath_routes) {
520 r = route_dup(route, &c->routes[i]);
884a63d7
YW
521 if (r < 0)
522 return r;
523
3b6a3bde 524 route_apply_multipath_route(c->routes[i], m);
884a63d7 525
3b6a3bde
YW
526 r = multipath_route_get_link(manager, m, &c->links[i]);
527 if (r < 0)
528 return r;
529
530 i++;
884a63d7
YW
531 }
532
3b6a3bde
YW
533 *ret = TAKE_PTR(c);
534 return 1;
884a63d7
YW
535}
536
3b6a3bde
YW
537void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router) {
538 Route *route;
d94e8ba0 539
3b6a3bde 540 assert(link);
d94e8ba0 541
3b6a3bde
YW
542 SET_FOREACH(route, link->routes) {
543 if (route->source != source)
544 continue;
11046cea 545
3b6a3bde
YW
546 if (source == NETWORK_CONFIG_SOURCE_NDISC &&
547 router && !in6_addr_equal(router, &route->provider.in6))
548 continue;
11046cea 549
3b6a3bde
YW
550 route_mark(route);
551 }
11046cea
YW
552}
553
b19afdfe 554static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
c71384a9 555 _cleanup_free_ char *state = NULL, *gw_alloc = NULL, *prefsrc = NULL,
b07d8145 556 *table = NULL, *scope = NULL, *proto = NULL, *flags = NULL;
c71384a9 557 const char *gw = NULL, *dst, *src;
7653a9dc 558
167a5561
YW
559 assert(route);
560 assert(str);
b19afdfe 561 assert(manager);
167a5561
YW
562
563 /* link may be NULL. */
564
7653a9dc
YW
565 if (!DEBUG_LOGGING)
566 return;
567
3b6a3bde 568 (void) network_config_state_to_string_alloc(route->state, &state);
c71384a9
ZJS
569
570 dst = in_addr_is_set(route->family, &route->dst) || route->dst_prefixlen > 0 ?
571 IN_ADDR_PREFIX_TO_STRING(route->family, &route->dst, route->dst_prefixlen) : NULL;
572 src = in_addr_is_set(route->family, &route->src) || route->src_prefixlen > 0 ?
573 IN_ADDR_PREFIX_TO_STRING(route->family, &route->src, route->src_prefixlen) : NULL;
574
b19afdfe
YW
575 if (in_addr_is_set(route->gw_family, &route->gw)) {
576 (void) in_addr_to_string(route->gw_family, &route->gw, &gw_alloc);
577 gw = gw_alloc;
578 } else if (route->gateway_from_dhcp_or_ra) {
579 if (route->gw_family == AF_INET)
580 gw = "_dhcp4";
581 else if (route->gw_family == AF_INET6)
582 gw = "_ipv6ra";
583 } else {
584 MultipathRoute *m;
585
586 ORDERED_SET_FOREACH(m, route->multipath_routes) {
4304f68d 587 _cleanup_free_ char *buf = NULL;
b19afdfe
YW
588 union in_addr_union a = m->gateway.address;
589
590 (void) in_addr_to_string(m->gateway.family, &a, &buf);
4304f68d
YW
591 (void) strextend_with_separator(&gw_alloc, ",", strna(buf));
592 if (m->ifname)
593 (void) strextend(&gw_alloc, "@", m->ifname);
594 else if (m->ifindex > 0)
c0f86d66 595 (void) strextendf(&gw_alloc, "@%i", m->ifindex);
4304f68d
YW
596 /* See comments in config_parse_multipath_route(). */
597 (void) strextendf(&gw_alloc, ":%"PRIu32, m->weight + 1);
b19afdfe
YW
598 }
599 gw = gw_alloc;
600 }
7653a9dc
YW
601 if (in_addr_is_set(route->family, &route->prefsrc))
602 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
603 (void) route_scope_to_string_alloc(route->scope, &scope);
b19afdfe 604 (void) manager_get_route_table_to_string(manager, route->table, &table);
7653a9dc 605 (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
b07d8145 606 (void) route_flags_to_string_alloc(route->flags, &flags);
7653a9dc
YW
607
608 log_link_debug(link,
3b6a3bde 609 "%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, "
b07d8145 610 "proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32", flags: %s",
3b6a3bde
YW
611 str, strna(network_config_source_to_string(route->source)), strna(state),
612 strna(dst), strna(src), strna(gw), strna(prefsrc),
7653a9dc
YW
613 strna(scope), strna(table), strna(proto),
614 strna(route_type_to_string(route->type)),
b07d8145 615 route->nexthop_id, route->priority, strna(flags));
167a5561
YW
616}
617
5f4d7aa4 618static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
5f4d7aa4
YW
619 int r;
620
621 assert(route);
622 assert(req);
623
624 /* link may be NULL */
625
3c7f1c07 626 if (in_addr_is_set(route->gw_family, &route->gw) && route->nexthop_id == 0) {
5f4d7aa4
YW
627 if (route->gw_family == route->family) {
628 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
629 if (r < 0)
3bcb5dc1 630 return r;
5f4d7aa4
YW
631 } else {
632 RouteVia rtvia = {
633 .family = route->gw_family,
634 .address = route->gw,
635 };
636
637 r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
638 if (r < 0)
3bcb5dc1 639 return r;
5f4d7aa4
YW
640 }
641 }
642
643 if (route->dst_prefixlen > 0) {
644 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
645 if (r < 0)
3bcb5dc1 646 return r;
5f4d7aa4
YW
647
648 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
649 if (r < 0)
3bcb5dc1 650 return r;
5f4d7aa4
YW
651 }
652
653 if (route->src_prefixlen > 0) {
654 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
655 if (r < 0)
3bcb5dc1 656 return r;
5f4d7aa4
YW
657
658 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
659 if (r < 0)
3bcb5dc1 660 return r;
5f4d7aa4
YW
661 }
662
94876904 663 if (in_addr_is_set(route->family, &route->prefsrc)) {
5f4d7aa4
YW
664 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
665 if (r < 0)
3bcb5dc1 666 return r;
5f4d7aa4
YW
667 }
668
669 r = sd_rtnl_message_route_set_scope(req, route->scope);
670 if (r < 0)
3bcb5dc1 671 return r;
5f4d7aa4 672
17f8d8f9 673 r = sd_rtnl_message_route_set_flags(req, route->flags & RTNH_F_ONLINK);
5f4d7aa4 674 if (r < 0)
3bcb5dc1 675 return r;
5f4d7aa4 676
3e0eeb8e
YW
677 if (route->table < 256) {
678 r = sd_rtnl_message_route_set_table(req, route->table);
679 if (r < 0)
3bcb5dc1 680 return r;
3e0eeb8e
YW
681 } else {
682 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
683 if (r < 0)
3bcb5dc1 684 return r;
5f4d7aa4 685
3e0eeb8e 686 /* Table attribute to allow more than 256. */
af316703 687 r = sd_netlink_message_append_u32(req, RTA_TABLE, route->table);
3e0eeb8e 688 if (r < 0)
3bcb5dc1 689 return r;
5f4d7aa4
YW
690 }
691
08c2fcdc
YW
692 if (!route_type_is_reject(route) &&
693 route->nexthop_id == 0 &&
694 ordered_set_isempty(route->multipath_routes)) {
5f4d7aa4
YW
695 assert(link); /* Those routes must be attached to a specific link */
696
697 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
698 if (r < 0)
3bcb5dc1 699 return r;
5f4d7aa4
YW
700 }
701
324e3422
YW
702 if (route->nexthop_id > 0) {
703 r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id);
704 if (r < 0)
3bcb5dc1 705 return r;
324e3422
YW
706 }
707
5f4d7aa4
YW
708 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
709 if (r < 0)
3bcb5dc1 710 return r;
5f4d7aa4
YW
711
712 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
713 if (r < 0)
3bcb5dc1 714 return r;
5f4d7aa4
YW
715
716 return 0;
717}
718
3b6a3bde 719static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
720 int r;
721
722 assert(m);
4645ad47 723
3b6a3bde 724 /* link may be NULL. */
76c5a0f2 725
3b6a3bde 726 if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
5a07fa9d
YW
727 return 0;
728
729 r = sd_netlink_message_get_errno(m);
730 if (r < 0 && r != -ESRCH)
f4cc1364 731 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
5a07fa9d
YW
732
733 return 1;
734}
735
3b6a3bde 736int route_remove(Route *route) {
4afd3348 737 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
c3fa1257 738 unsigned char type;
3b6a3bde
YW
739 Manager *manager;
740 Link *link;
5c1d3fc9
UTL
741 int r;
742
3b6a3bde
YW
743 assert(route);
744 assert(route->manager || (route->link && route->link->manager));
4c701096 745 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9 746
3b6a3bde
YW
747 link = route->link;
748 manager = route->manager ?: link->manager;
ad208fac 749
552b90a2 750 log_route_debug(route, "Removing", link, manager);
167a5561 751
ad208fac 752 r = sd_rtnl_message_new_route(manager->rtnl, &req,
28cc555d
DW
753 RTM_DELROUTE, route->family,
754 route->protocol);
f647962d 755 if (r < 0)
3bcb5dc1 756 return log_link_error_errno(link, r, "Could not create netlink message: %m");
5c1d3fc9 757
c3fa1257
YW
758 if (route->family == AF_INET && route->nexthop_id > 0 && route->type == RTN_BLACKHOLE)
759 /* When IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel
760 * sends RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type
761 * fib_rt_info::type may not be blackhole. Thus, we cannot know the internal value.
762 * Moreover, on route removal, the matching is done with the hidden value if we set
763 * non-zero type in RTM_DELROUTE message. Note, sd_rtnl_message_new_route() sets
764 * RTN_UNICAST by default. So, we need to clear the type here. */
765 type = RTN_UNSPEC;
766 else
767 type = route->type;
768
769 r = sd_rtnl_message_route_set_type(req, type);
770 if (r < 0)
771 return log_link_error_errno(link, r, "Could not set route type: %m");
772
5f4d7aa4 773 r = route_set_netlink_message(route, req, link);
f647962d 774 if (r < 0)
3bcb5dc1 775 return log_error_errno(r, "Could not fill netlink message: %m");
5c1d3fc9 776
3b6a3bde
YW
777 r = netlink_call_async(manager->rtnl, NULL, req, route_remove_handler,
778 link ? link_netlink_destroy_callback : NULL, link);
779 if (r < 0)
3bcb5dc1 780 return log_link_error_errno(link, r, "Could not send netlink message: %m");
76c5a0f2 781
3b6a3bde 782 link_ref(link);
563c69c6 783
3b6a3bde 784 route_enter_removing(route);
5c1d3fc9
UTL
785 return 0;
786}
787
b4564f4e 788static void manager_mark_routes(Manager *manager, bool foreign, const Link *except) {
3b6a3bde
YW
789 Route *route;
790 Link *link;
b4564f4e 791 int r;
779804dd 792
3b6a3bde 793 assert(manager);
779804dd 794
3b6a3bde
YW
795 /* First, mark all routes. */
796 SET_FOREACH(route, manager->routes) {
797 /* Do not touch routes managed by the kernel. */
798 if (route->protocol == RTPROT_KERNEL)
799 continue;
779804dd 800
a0e99a37
YW
801 /* When 'foreign' is true, mark only foreign routes, and vice versa. */
802 if (foreign != (route->source == NETWORK_CONFIG_SOURCE_FOREIGN))
3b6a3bde 803 continue;
779804dd 804
d0f17cfd
YW
805 /* Do not touch dynamic routes. They will removed by dhcp_pd_prefix_lost() */
806 if (IN_SET(route->source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6))
807 continue;
808
3b6a3bde
YW
809 /* Ignore routes not assigned yet or already removed. */
810 if (!route_exists(route))
811 continue;
1132a714 812
3b6a3bde
YW
813 route_mark(route);
814 }
1132a714 815
3b6a3bde 816 /* Then, unmark all routes requested by active links. */
6eab614d 817 HASHMAP_FOREACH(link, manager->links_by_index) {
1132a714
YW
818 if (link == except)
819 continue;
820
3b6a3bde
YW
821 if (!link->network)
822 continue;
1132a714 823
3b6a3bde
YW
824 if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
825 continue;
1132a714 826
3b6a3bde
YW
827 HASHMAP_FOREACH(route, link->network->routes_by_section) {
828 _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
829 Route *existing;
1132a714 830
3b6a3bde
YW
831 r = route_convert(manager, route, &converted);
832 if (r < 0)
833 continue;
834 if (r == 0) {
835 if (route_get(manager, NULL, route, &existing) >= 0)
836 route_unmark(existing);
837 continue;
838 }
8a9ce525 839
3b6a3bde
YW
840 for (size_t i = 0; i < converted->n; i++)
841 if (route_get(manager, NULL, converted->routes[i], &existing) >= 0)
842 route_unmark(existing);
843 }
844 }
b4564f4e
YW
845}
846
a0e99a37 847static int manager_drop_marked_routes(Manager *manager) {
b4564f4e
YW
848 Route *route;
849 int k, r = 0;
850
851 assert(manager);
1132a714 852
3b6a3bde
YW
853 SET_FOREACH(route, manager->routes) {
854 if (!route_is_marked(route))
1132a714
YW
855 continue;
856
3b6a3bde 857 k = route_remove(route);
1132a714
YW
858 if (k < 0 && r >= 0)
859 r = k;
860 }
861
862 return r;
863}
864
dbf8942a
YW
865static bool route_by_kernel(const Route *route) {
866 assert(route);
867
868 if (route->protocol == RTPROT_KERNEL)
869 return true;
870
2b3a8e28
YW
871 /* The kernels older than a826b04303a40d52439aa141035fca5654ccaccd (v5.11) create the IPv6
872 * multicast with RTPROT_BOOT. Do not touch it. */
dbf8942a
YW
873 if (route->protocol == RTPROT_BOOT &&
874 route->family == AF_INET6 &&
875 route->dst_prefixlen == 8 &&
876 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 }}}))
877 return true;
878
879 return false;
880}
881
e9084344
YW
882static void link_unmark_wireguard_routes(Link *link) {
883 Route *route, *existing;
e9084344
YW
884 Wireguard *w;
885
886 assert(link);
887
9aa2585e 888 w = WIREGUARD(link->netdev);
e9084344
YW
889 if (!w)
890 return;
891
892 SET_FOREACH(route, w->routes)
893 if (route_get(NULL, link, route, &existing) >= 0)
894 route_unmark(existing);
895}
896
779804dd
YW
897int link_drop_foreign_routes(Link *link) {
898 Route *route;
3b6a3bde 899 int k, r;
779804dd
YW
900
901 assert(link);
1132a714 902 assert(link->manager);
8a20a843 903 assert(link->network);
779804dd 904
3b6a3bde 905 SET_FOREACH(route, link->routes) {
779804dd 906 /* do not touch routes managed by the kernel */
dbf8942a 907 if (route_by_kernel(route))
779804dd
YW
908 continue;
909
3b6a3bde
YW
910 /* Do not remove routes we configured. */
911 if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
912 continue;
913
914 /* Ignore routes not assigned yet or already removed. */
915 if (!route_exists(route))
916 continue;
917
8a20a843 918 if (route->protocol == RTPROT_STATIC &&
779804dd
YW
919 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
920 continue;
921
8a20a843 922 if (route->protocol == RTPROT_DHCP &&
779804dd
YW
923 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
924 continue;
925
3b6a3bde
YW
926 route_mark(route);
927 }
928
929 HASHMAP_FOREACH(route, link->network->routes_by_section) {
930 _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
931 Route *existing;
932
933 r = route_convert(link->manager, route, &converted);
934 if (r < 0)
935 continue;
936 if (r == 0) {
937 if (route_get(NULL, link, route, &existing) >= 0)
938 route_unmark(existing);
939 continue;
940 }
941
942 for (size_t i = 0; i < converted->n; i++)
943 if (route_get(NULL, link, converted->routes[i], &existing) >= 0)
944 route_unmark(existing);
945 }
946
e9084344
YW
947 link_unmark_wireguard_routes(link);
948
3b6a3bde
YW
949 r = 0;
950 SET_FOREACH(route, link->routes) {
951 if (!route_is_marked(route))
952 continue;
953
954 k = route_remove(route);
779804dd
YW
955 if (k < 0 && r >= 0)
956 r = k;
957 }
958
b4564f4e
YW
959 manager_mark_routes(link->manager, /* foreign = */ true, NULL);
960
a0e99a37 961 k = manager_drop_marked_routes(link->manager);
1132a714
YW
962 if (k < 0 && r >= 0)
963 r = k;
964
779804dd
YW
965 return r;
966}
967
a0e99a37 968int link_drop_managed_routes(Link *link) {
62f0ea5f
YW
969 Route *route;
970 int k, r = 0;
971
972 assert(link);
973
974 SET_FOREACH(route, link->routes) {
975 /* do not touch routes managed by the kernel */
3b6a3bde 976 if (route_by_kernel(route))
62f0ea5f
YW
977 continue;
978
a0e99a37
YW
979 /* Do not touch routes managed by kernel or other tools. */
980 if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)
981 continue;
982
3b6a3bde
YW
983 if (!route_exists(route))
984 continue;
985
986 k = route_remove(route);
62f0ea5f
YW
987 if (k < 0 && r >= 0)
988 r = k;
989 }
990
b4564f4e
YW
991 manager_mark_routes(link->manager, /* foreign = */ false, link);
992
a0e99a37 993 k = manager_drop_marked_routes(link->manager);
1132a714
YW
994 if (k < 0 && r >= 0)
995 r = k;
996
62f0ea5f
YW
997 return r;
998}
999
b4564f4e
YW
1000void link_foreignize_routes(Link *link) {
1001 Route *route;
1002
1003 assert(link);
1004
1005 SET_FOREACH(route, link->routes)
1006 route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
1007
1008 manager_mark_routes(link->manager, /* foreign = */ false, link);
1009
1010 SET_FOREACH(route, link->manager->routes) {
1011 if (!route_is_marked(route))
1012 continue;
1013
1014 route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
1015 }
1016}
1017
74154c2e 1018static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
99534007 1019 Route *route = ASSERT_PTR(userdata);
70b06526 1020 Link *link;
f833694d
TG
1021 int r;
1022
70b06526
YW
1023 assert(route->manager || (route->link && route->link->manager));
1024
1025 link = route->link; /* This may be NULL. */
f833694d 1026
3b6a3bde 1027 r = route_remove(route);
d6ad41e2 1028 if (r < 0) {
70b06526
YW
1029 log_link_warning_errno(link, r, "Could not remove route: %m");
1030 if (link)
1031 link_enter_failed(link);
d6ad41e2 1032 }
f833694d
TG
1033
1034 return 1;
1035}
1036
0b0c81bb 1037static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo) {
3b6a3bde 1038 Manager *manager;
0b0c81bb 1039 int r;
f9bb3338 1040
228c3e21 1041 assert(route);
3b6a3bde 1042 assert(route->manager || (route->link && route->link->manager));
f9bb3338 1043
3b6a3bde 1044 manager = route->manager ?: route->link->manager;
228c3e21 1045
91fc5135 1046 if (route->lifetime_usec == USEC_INFINITY)
3b6a3bde 1047 return 0;
228c3e21 1048
0b0c81bb
YW
1049 if (cacheinfo && cacheinfo->rta_expires != 0)
1050 /* Assume that non-zero rta_expires means kernel will handle the route expiration. */
3b6a3bde 1051 return 0;
228c3e21 1052
ba4e0427 1053 r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME,
91fc5135 1054 route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true);
0b0c81bb
YW
1055 if (r < 0)
1056 return r;
228c3e21 1057
0b0c81bb 1058 return 1;
f9bb3338
YW
1059}
1060
08c2fcdc 1061static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
6ff5cc6b
YW
1062 struct rtnexthop *rtnh;
1063 struct rtattr *new_rta;
1064 int r;
1065
1066 assert(route);
1067 assert(m);
1068 assert(rta);
1069 assert(*rta);
1070
1071 new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
1072 if (!new_rta)
1073 return -ENOMEM;
1074 *rta = new_rta;
1075
1076 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1077 *rtnh = (struct rtnexthop) {
1078 .rtnh_len = sizeof(*rtnh),
08c2fcdc 1079 .rtnh_ifindex = m->ifindex > 0 ? m->ifindex : link->ifindex,
234106db 1080 .rtnh_hops = m->weight,
6ff5cc6b
YW
1081 };
1082
1083 (*rta)->rta_len += sizeof(struct rtnexthop);
1084
1085 if (route->family == m->gateway.family) {
1086 r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
1087 if (r < 0)
1088 goto clear;
1089 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1090 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
1091 } else {
1092 r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1093 if (r < 0)
1094 goto clear;
1095 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1096 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1097 }
1098
1099 return 0;
1100
1101clear:
1102 (*rta)->rta_len -= sizeof(struct rtnexthop);
1103 return r;
1104}
1105
08c2fcdc 1106static int append_nexthops(const Link *link, const Route *route, sd_netlink_message *req) {
6ff5cc6b
YW
1107 _cleanup_free_ struct rtattr *rta = NULL;
1108 struct rtnexthop *rtnh;
1109 MultipathRoute *m;
1110 size_t offset;
6ff5cc6b
YW
1111 int r;
1112
08c2fcdc
YW
1113 assert(link);
1114 assert(route);
1115 assert(req);
1116
6ff5cc6b
YW
1117 if (ordered_set_isempty(route->multipath_routes))
1118 return 0;
1119
1120 rta = new(struct rtattr, 1);
1121 if (!rta)
1122 return -ENOMEM;
1123
1124 *rta = (struct rtattr) {
1125 .rta_type = RTA_MULTIPATH,
1126 .rta_len = RTA_LENGTH(0),
1127 };
1128 offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
1129
90e74a66 1130 ORDERED_SET_FOREACH(m, route->multipath_routes) {
08c2fcdc 1131 r = append_nexthop_one(link, route, m, &rta, offset);
6ff5cc6b
YW
1132 if (r < 0)
1133 return r;
1134
1135 rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
1136 offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
1137 }
1138
1139 r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
1140 if (r < 0)
1141 return r;
1142
1143 return 0;
1144}
1145
5a07fa9d
YW
1146int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
1147 int r;
1148
1149 assert(m);
1150 assert(link);
1151 assert(error_msg);
1152
5a07fa9d
YW
1153 r = sd_netlink_message_get_errno(m);
1154 if (r < 0 && r != -EEXIST) {
e95c06c9 1155 log_link_message_warning_errno(link, m, r, "Could not set route");
5a07fa9d
YW
1156 link_enter_failed(link);
1157 return 0;
1158 }
1159
1160 return 1;
1161}
1162
b9e4ec50 1163static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) {
a79a8d16 1164 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
7b3a7581 1165 int r;
f579559b 1166
3b6a3bde
YW
1167 assert(route);
1168 assert(IN_SET(route->family, AF_INET, AF_INET6));
f579559b 1169 assert(link);
f882c247
TG
1170 assert(link->manager);
1171 assert(link->manager->rtnl);
f579559b 1172 assert(link->ifindex > 0);
54ff39f7 1173 assert(req);
1b566071 1174
552b90a2 1175 log_route_debug(route, "Configuring", link, link->manager);
156ed65e 1176
a79a8d16 1177 r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol);
f647962d 1178 if (r < 0)
a79a8d16 1179 return r;
f579559b 1180
a79a8d16 1181 r = sd_rtnl_message_route_set_type(m, route->type);
c3fa1257 1182 if (r < 0)
a79a8d16 1183 return r;
c3fa1257 1184
a79a8d16 1185 r = route_set_netlink_message(route, m, link);
f647962d 1186 if (r < 0)
a79a8d16 1187 return r;
3b015d40 1188
b9e4ec50
YW
1189 if (lifetime_sec != UINT32_MAX) {
1190 r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec);
1191 if (r < 0)
1192 return r;
f02ba163
DD
1193 }
1194
9b88f20a 1195 if (route->ttl_propagate >= 0) {
a79a8d16 1196 r = sd_netlink_message_append_u8(m, RTA_TTL_PROPAGATE, route->ttl_propagate);
9b88f20a 1197 if (r < 0)
a79a8d16 1198 return r;
9b88f20a
SS
1199 }
1200
a79a8d16 1201 r = sd_netlink_message_open_container(m, RTA_METRICS);
d6fceaf1 1202 if (r < 0)
a79a8d16 1203 return r;
d6fceaf1
SS
1204
1205 if (route->mtu > 0) {
a79a8d16 1206 r = sd_netlink_message_append_u32(m, RTAX_MTU, route->mtu);
d6fceaf1 1207 if (r < 0)
a79a8d16 1208 return r;
d6fceaf1
SS
1209 }
1210
6b21ad33 1211 if (route->initcwnd > 0) {
a79a8d16 1212 r = sd_netlink_message_append_u32(m, RTAX_INITCWND, route->initcwnd);
323d9329 1213 if (r < 0)
a79a8d16 1214 return r;
323d9329
SS
1215 }
1216
6b21ad33 1217 if (route->initrwnd > 0) {
a79a8d16 1218 r = sd_netlink_message_append_u32(m, RTAX_INITRWND, route->initrwnd);
323d9329 1219 if (r < 0)
a79a8d16 1220 return r;
323d9329
SS
1221 }
1222
67c193bf 1223 if (route->quickack >= 0) {
a79a8d16 1224 r = sd_netlink_message_append_u32(m, RTAX_QUICKACK, route->quickack);
09f5dfad 1225 if (r < 0)
a79a8d16 1226 return r;
09f5dfad
SS
1227 }
1228
633c7258 1229 if (route->fast_open_no_cookie >= 0) {
a79a8d16 1230 r = sd_netlink_message_append_u32(m, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
633c7258 1231 if (r < 0)
a79a8d16 1232 return r;
633c7258
SS
1233 }
1234
007cac09 1235 if (route->advmss > 0) {
a79a8d16 1236 r = sd_netlink_message_append_u32(m, RTAX_ADVMSS, route->advmss);
007cac09 1237 if (r < 0)
a79a8d16 1238 return r;
007cac09
SS
1239 }
1240
dc7c21f0
YW
1241 if (!isempty(route->tcp_congestion_control_algo)) {
1242 r = sd_netlink_message_append_string(m, RTAX_CC_ALGO, route->tcp_congestion_control_algo);
1243 if (r < 0)
1244 return r;
1245 }
1246
a79a8d16 1247 r = sd_netlink_message_close_container(m);
d6fceaf1 1248 if (r < 0)
a79a8d16 1249 return r;
d6fceaf1 1250
228c3e21
YW
1251 if (!ordered_set_isempty(route->multipath_routes)) {
1252 assert(route->nexthop_id == 0);
1253 assert(!in_addr_is_set(route->gw_family, &route->gw));
f833694d 1254
a79a8d16 1255 r = append_nexthops(link, route, m);
3c7f1c07 1256 if (r < 0)
a79a8d16 1257 return r;
f9bb3338 1258 }
80b0e860 1259
80d62d4f 1260 return request_call_netlink_async(link->manager->rtnl, m, req);
3b6a3bde 1261}
0c54bfd6 1262
8bed7c55
YW
1263static int route_is_ready_to_configure(const Route *route, Link *link) {
1264 int r;
1265
1266 assert(route);
1267 assert(link);
1268
1269 if (!link_is_ready_to_configure(link, false))
1270 return false;
1271
1272 if (set_size(link->routes) >= routes_max())
1273 return false;
1274
1275 if (route->nexthop_id > 0) {
1276 struct nexthop_grp *nhg;
1277 NextHop *nh;
1278
1279 if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
1280 return false;
1281
1282 if (!nexthop_exists(nh))
1283 return false;
1284
1285 HASHMAP_FOREACH(nhg, nh->group) {
1286 NextHop *g;
1287
1288 if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0)
1289 return false;
1290
1291 if (!nexthop_exists(g))
1292 return false;
1293 }
1294 }
1295
1296 if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
1297 r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6);
1298 if (r <= 0)
1299 return r;
1300 }
1301
1302 if (!gateway_is_ready(link, FLAGS_SET(route->flags, RTNH_F_ONLINK), route->gw_family, &route->gw))
1303 return false;
1304
1305 MultipathRoute *m;
1306 ORDERED_SET_FOREACH(m, route->multipath_routes) {
1307 union in_addr_union a = m->gateway.address;
1308 Link *l = NULL;
1309
1310 r = multipath_route_get_link(link->manager, m, &l);
1311 if (r < 0)
1312 return false;
1313 if (r > 0) {
2fd3908b
YW
1314 if (!link_is_ready_to_configure(l, /* allow_unmanaged = */ true) ||
1315 !link_has_carrier(l))
8bed7c55
YW
1316 return false;
1317
1318 m->ifindex = l->ifindex;
1319 }
1320
1321 if (!gateway_is_ready(l ?: link, FLAGS_SET(route->flags, RTNH_F_ONLINK), m->gateway.family, &a))
1322 return false;
1323 }
1324
1325 return true;
1326}
1327
09d09207 1328static int route_process_request(Request *req, Link *link, Route *route) {
8bed7c55 1329 _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
8bed7c55
YW
1330 int r;
1331
1332 assert(req);
ff51134c 1333 assert(link);
b9e4ec50 1334 assert(link->manager);
ff51134c 1335 assert(route);
8bed7c55
YW
1336
1337 r = route_is_ready_to_configure(route, link);
1338 if (r < 0)
1339 return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m");
1340 if (r == 0)
1341 return 0;
1342
1343 if (route_needs_convert(route)) {
1344 r = route_convert(link->manager, route, &converted);
1345 if (r < 0)
1346 return log_link_warning_errno(link, r, "Failed to convert route: %m");
1347
1348 assert(r > 0);
1349 assert(converted);
1350
1351 for (size_t i = 0; i < converted->n; i++) {
1352 Route *existing;
1353
1354 if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) {
1355 _cleanup_(route_freep) Route *tmp = NULL;
1356
1357 r = route_dup(converted->routes[i], &tmp);
1358 if (r < 0)
1359 return log_oom();
1360
1361 r = route_add(link->manager, converted->links[i] ?: link, tmp);
1362 if (r < 0)
1363 return log_link_warning_errno(link, r, "Failed to add route: %m");
1364
1365 TAKE_PTR(tmp);
1366 } else {
1367 existing->source = converted->routes[i]->source;
1368 existing->provider = converted->routes[i]->provider;
1369 }
1370 }
1371 }
1372
b9e4ec50
YW
1373 usec_t now_usec;
1374 assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
1375 uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
1376 if (sec == 0) {
1377 log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
1378 network_config_source_to_string(route->source));
1379
1380 if (converted)
1381 for (size_t i = 0; i < converted->n; i++) {
1382 Route *existing;
1383
1384 assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
1385 route_cancel_requesting(existing);
1386 }
1387 else
1388 route_cancel_requesting(route);
1389 }
1390
1391 r = route_configure(route, sec, link, req);
8bed7c55
YW
1392 if (r < 0)
1393 return log_link_warning_errno(link, r, "Failed to configure route: %m");
1394
1395 if (converted)
1396 for (size_t i = 0; i < converted->n; i++) {
1397 Route *existing;
1398
1399 assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
1400 route_enter_configuring(existing);
1401 }
1402 else
1403 route_enter_configuring(route);
1404
1405 return 1;
1406}
1407
76c5a0f2
YW
1408int link_request_route(
1409 Link *link,
1410 Route *route,
1411 bool consume_object,
1412 unsigned *message_counter,
80d62d4f 1413 route_netlink_handler_t netlink_handler,
76c5a0f2 1414 Request **ret) {
f345918d 1415
3b6a3bde
YW
1416 Route *existing;
1417 int r;
1418
f345918d 1419 assert(link);
76c5a0f2 1420 assert(link->manager);
f345918d 1421 assert(route);
3b6a3bde
YW
1422 assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
1423 assert(!route_needs_convert(route));
1424
1425 if (route_get(link->manager, link, route, &existing) < 0) {
1426 _cleanup_(route_freep) Route *tmp = NULL;
1427
1428 if (consume_object)
1429 tmp = route;
1430 else {
1431 r = route_dup(route, &tmp);
1432 if (r < 0)
1433 return r;
1434 }
1435
1436 r = route_add(link->manager, link, tmp);
1437 if (r < 0)
1438 return r;
1439
1440 existing = TAKE_PTR(tmp);
1441 } else {
1442 existing->source = route->source;
1443 existing->provider = route->provider;
c1c655bb 1444 existing->lifetime_usec = route->lifetime_usec;
3b6a3bde
YW
1445 if (consume_object)
1446 route_free(route);
42e7ce69
YW
1447
1448 if (existing->expire) {
1449 /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE
1450 * message, so we need to update the timer here. */
1451 r = route_setup_timer(existing, NULL);
1452 if (r < 0)
1453 log_link_warning_errno(link, r, "Failed to update expiration timer for route, ignoring: %m");
1454 if (r > 0)
1455 log_route_debug(existing, "Updated expiration timer for", link, link->manager);
1456 }
3b6a3bde
YW
1457 }
1458
1459 log_route_debug(existing, "Requesting", link, link->manager);
09d09207
YW
1460 r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
1461 existing, NULL,
1462 route_hash_func,
1463 route_compare_func,
1464 route_process_request,
1465 message_counter, netlink_handler, ret);
3b6a3bde
YW
1466 if (r <= 0)
1467 return r;
1468
1469 route_enter_requesting(existing);
1470 return 1;
1471}
1472
80d62d4f 1473static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
e1e4cd1e
YW
1474 int r;
1475
1476 assert(link);
e1e4cd1e
YW
1477
1478 r = route_configure_handler_internal(rtnl, m, link, "Could not set route");
1479 if (r <= 0)
1480 return r;
1481
1482 if (link->static_route_messages == 0) {
1483 log_link_debug(link, "Routes set");
1484 link->static_routes_configured = true;
1485 link_check_ready(link);
1486 }
1487
1488 return 1;
1489}
1490
3b6a3bde
YW
1491static int link_request_static_route(Link *link, Route *route) {
1492 assert(link);
1493 assert(link->manager);
1494 assert(route);
1495
1496 if (!route_needs_convert(route))
1497 return link_request_route(link, route, false, &link->static_route_messages,
1498 static_route_handler, NULL);
f345918d 1499
76c5a0f2 1500 log_route_debug(route, "Requesting", link, link->manager);
09d09207
YW
1501 return link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
1502 route, NULL, route_hash_func, route_compare_func,
1503 route_process_request,
1504 &link->static_route_messages, static_route_handler, NULL);
f345918d
YW
1505}
1506
e9084344
YW
1507static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
1508 NetDev *netdev;
1509 Wireguard *w;
1510 Route *route;
1511 int r;
1512
1513 assert(link);
1514
1515 if (!streq_ptr(link->kind, "wireguard"))
1516 return 0;
1517
1518 if (netdev_get(link->manager, link->ifname, &netdev) < 0)
1519 return 0;
1520
1521 w = WIREGUARD(netdev);
1522 if (!w)
1523 return 0;
1524
1525 SET_FOREACH(route, w->routes) {
1526 if (only_ipv4 && route->family != AF_INET)
1527 continue;
1528
1529 r = link_request_static_route(link, route);
1530 if (r < 0)
1531 return r;
1532 }
1533
1534 return 0;
1535}
1536
76c5a0f2 1537int link_request_static_routes(Link *link, bool only_ipv4) {
e8f52f3c 1538 Route *route;
141318f7
YW
1539 int r;
1540
f345918d
YW
1541 assert(link);
1542 assert(link->network);
1543
76c5a0f2 1544 link->static_routes_configured = false;
e8f52f3c 1545
76c5a0f2 1546 HASHMAP_FOREACH(route, link->network->routes_by_section) {
e8f52f3c
YW
1547 if (route->gateway_from_dhcp_or_ra)
1548 continue;
1549
76c5a0f2 1550 if (only_ipv4 && route->family != AF_INET)
f345918d
YW
1551 continue;
1552
3b6a3bde 1553 r = link_request_static_route(link, route);
f345918d 1554 if (r < 0)
76c5a0f2
YW
1555 return r;
1556 }
f345918d 1557
e9084344
YW
1558 r = link_request_wireguard_routes(link, only_ipv4);
1559 if (r < 0)
1560 return r;
1561
76c5a0f2
YW
1562 if (link->static_route_messages == 0) {
1563 link->static_routes_configured = true;
1564 link_check_ready(link);
1565 } else {
1566 log_link_debug(link, "Requesting routes");
1567 link_set_state(link, LINK_STATE_CONFIGURING);
f345918d
YW
1568 }
1569
1570 return 0;
1571}
1572
e1e4cd1e
YW
1573void route_cancel_request(Route *route, Link *link) {
1574 Request req;
1575
1576 assert(route);
1577
1578 link = route->link ?: link;
1579
1580 assert(link);
1581
1582 if (!route_is_requesting(route))
1583 return;
1584
1585 req = (Request) {
1586 .link = link,
1587 .type = REQUEST_TYPE_ROUTE,
09d09207
YW
1588 .userdata = route,
1589 .hash_func = (hash_func_t) route_hash_func,
1590 .compare_func = (compare_func_t) route_compare_func,
e1e4cd1e
YW
1591 };
1592
e26d3d40 1593 request_detach(link->manager, &req);
e1e4cd1e
YW
1594 route_cancel_requesting(route);
1595}
1596
0b0c81bb
YW
1597static int process_route_one(
1598 Manager *manager,
1599 Link *link,
1600 uint16_t type,
1601 Route *in,
1602 const struct rta_cacheinfo *cacheinfo) {
1603
3b6a3bde 1604 _cleanup_(route_freep) Route *tmp = in;
f9bb3338
YW
1605 Route *route = NULL;
1606 int r;
1607
1608 assert(manager);
1609 assert(tmp);
1610 assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
1611
3b6a3bde 1612 /* link may be NULL. This consumes 'in'. */
f9bb3338
YW
1613
1614 (void) route_get(manager, link, tmp, &route);
1615
f9bb3338
YW
1616 switch (type) {
1617 case RTM_NEWROUTE:
3b6a3bde 1618 if (route) {
17f8d8f9 1619 route->flags = tmp->flags;
3b6a3bde
YW
1620 route_enter_configured(route);
1621 log_route_debug(route, "Received remembered", link, manager);
1622
0b0c81bb
YW
1623 r = route_setup_timer(route, cacheinfo);
1624 if (r < 0)
1625 log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m");
1626 if (r > 0)
1627 log_route_debug(route, "Configured expiration timer for", link, manager);
1628
3b6a3bde
YW
1629 } else if (!manager->manage_foreign_routes) {
1630 route_enter_configured(tmp);
1631 log_route_debug(tmp, "Ignoring received", link, manager);
1632
1633 } else {
1634 /* A route appeared that we did not request */
1635 route_enter_configured(tmp);
1636 log_route_debug(tmp, "Received new", link, manager);
1637 r = route_add(manager, link, tmp);
1638 if (r < 0) {
1639 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
1640 return 0;
f9bb3338 1641 }
3b6a3bde
YW
1642 TAKE_PTR(tmp);
1643 }
f9bb3338
YW
1644
1645 break;
1646
1647 case RTM_DELROUTE:
3b6a3bde
YW
1648 if (route) {
1649 route_enter_removed(route);
1650 if (route->state == 0) {
1651 log_route_debug(route, "Forgetting", link, manager);
1652 route_free(route);
1653 } else
1654 log_route_debug(route, "Removed", link, manager);
1655 } else
1656 log_route_debug(tmp,
1657 manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
1658 link, manager);
1659
f9bb3338
YW
1660 break;
1661
1662 default:
04499a70 1663 assert_not_reached();
f9bb3338
YW
1664 }
1665
1666 return 1;
1667}
1668
4468f01b 1669int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
3b6a3bde 1670 _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
4468f01b 1671 _cleanup_(route_freep) Route *tmp = NULL;
f9bb3338 1672 _cleanup_free_ void *rta_multipath = NULL;
0b0c81bb
YW
1673 struct rta_cacheinfo cacheinfo;
1674 bool has_cacheinfo;
4468f01b
YW
1675 Link *link = NULL;
1676 uint32_t ifindex;
1677 uint16_t type;
f9bb3338 1678 size_t rta_len;
4468f01b
YW
1679 int r;
1680
1681 assert(rtnl);
1682 assert(message);
1683 assert(m);
1684
1685 if (sd_netlink_message_is_error(message)) {
1686 r = sd_netlink_message_get_errno(message);
1687 if (r < 0)
1688 log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
1689
1690 return 0;
1691 }
1692
1693 r = sd_netlink_message_get_type(message, &type);
1694 if (r < 0) {
1695 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1696 return 0;
1697 } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
1698 log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
1699 return 0;
1700 }
1701
1702 r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
ad208fac 1703 if (r < 0 && r != -ENODATA) {
4468f01b
YW
1704 log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
1705 return 0;
ad208fac
YW
1706 } else if (r >= 0) {
1707 if (ifindex <= 0) {
c0f86d66 1708 log_warning("rtnl: received route message with invalid ifindex %u, ignoring.", ifindex);
ad208fac
YW
1709 return 0;
1710 }
4468f01b 1711
6eab614d 1712 r = link_get_by_index(m, ifindex, &link);
ad208fac
YW
1713 if (r < 0 || !link) {
1714 /* when enumerating we might be out of sync, but we will
1715 * get the route again, so just ignore it */
1716 if (!m->enumerating)
c0f86d66 1717 log_warning("rtnl: received route message for link (%u) we do not know about, ignoring", ifindex);
ad208fac
YW
1718 return 0;
1719 }
4468f01b
YW
1720 }
1721
1722 r = route_new(&tmp);
1723 if (r < 0)
1724 return log_oom();
1725
1726 r = sd_rtnl_message_route_get_family(message, &tmp->family);
1727 if (r < 0) {
1728 log_link_warning(link, "rtnl: received route message without family, ignoring");
1729 return 0;
1730 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
1731 log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
1732 return 0;
1733 }
1734
1735 r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
1736 if (r < 0) {
f505de80 1737 log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m");
4468f01b
YW
1738 return 0;
1739 }
1740
17f8d8f9
YW
1741 r = sd_rtnl_message_route_get_flags(message, &tmp->flags);
1742 if (r < 0) {
1743 log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m");
1744 return 0;
1745 }
1746
ad6df717
YW
1747 r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
1748 if (r < 0 && r != -ENODATA) {
1749 log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
1750 return 0;
1751 }
4468f01b 1752
ad6df717
YW
1753 r = netlink_message_read_in_addr_union(message, RTA_GATEWAY, tmp->family, &tmp->gw);
1754 if (r < 0 && r != -ENODATA) {
1755 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1756 return 0;
1757 } else if (r >= 0)
1758 tmp->gw_family = tmp->family;
1759 else if (tmp->family == AF_INET) {
1760 RouteVia via;
6dd53981
YW
1761
1762 r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
1763 if (r < 0 && r != -ENODATA) {
1764 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1765 return 0;
1766 } else if (r >= 0) {
1767 tmp->gw_family = via.family;
1768 tmp->gw = via.address;
4468f01b 1769 }
ad6df717 1770 }
4468f01b 1771
ad6df717
YW
1772 r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
1773 if (r < 0 && r != -ENODATA) {
1774 log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
1775 return 0;
1776 }
4468f01b 1777
ad6df717
YW
1778 r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
1779 if (r < 0 && r != -ENODATA) {
1780 log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
4468f01b
YW
1781 return 0;
1782 }
1783
1784 r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
1785 if (r < 0) {
1786 log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
1787 return 0;
1788 }
1789
1790 r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
1791 if (r < 0) {
1792 log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
1793 return 0;
1794 }
1795
1796 r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
1797 if (r < 0) {
1798 log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
1799 return 0;
1800 }
1801
1802 r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
1803 if (r < 0) {
1804 log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
1805 return 0;
1806 }
1807
1808 r = sd_rtnl_message_route_get_type(message, &tmp->type);
1809 if (r < 0) {
1810 log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
1811 return 0;
1812 }
1813
5e82a613
YW
1814 r = sd_netlink_message_read_u32(message, RTA_TABLE, &tmp->table);
1815 if (r == -ENODATA) {
f14a6e7f
YW
1816 unsigned char table;
1817
5e82a613
YW
1818 r = sd_rtnl_message_route_get_table(message, &table);
1819 if (r >= 0)
1820 tmp->table = table;
1821 }
4468f01b
YW
1822 if (r < 0) {
1823 log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
1824 return 0;
1825 }
4468f01b
YW
1826
1827 r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
1828 if (r < 0 && r != -ENODATA) {
1829 log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
1830 return 0;
1831 }
1832
324e3422
YW
1833 r = sd_netlink_message_read_u32(message, RTA_NH_ID, &tmp->nexthop_id);
1834 if (r < 0 && r != -ENODATA) {
1835 log_link_warning_errno(link, r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
1836 return 0;
1837 }
1838
4468f01b
YW
1839 r = sd_netlink_message_enter_container(message, RTA_METRICS);
1840 if (r < 0 && r != -ENODATA) {
f505de80 1841 log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container, ignoring: %m");
4468f01b
YW
1842 return 0;
1843 }
1844 if (r >= 0) {
1845 r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
1846 if (r < 0 && r != -ENODATA) {
1847 log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
1848 return 0;
1849 }
1850
1851 r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
1852 if (r < 0 && r != -ENODATA) {
1853 log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
1854 return 0;
386e8908
YW
1855 }
1856
1857 r = sd_netlink_message_read_u32(message, RTAX_ADVMSS, &tmp->advmss);
1858 if (r < 0 && r != -ENODATA) {
1859 log_link_warning_errno(link, r, "rtnl: received route message with invalid advmss, ignoring: %m");
1860 return 0;
4468f01b
YW
1861 }
1862
1863 r = sd_netlink_message_exit_container(message);
1864 if (r < 0) {
f505de80 1865 log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container, ignoring: %m");
4468f01b
YW
1866 return 0;
1867 }
1868 }
1869
f9bb3338
YW
1870 r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
1871 if (r < 0 && r != -ENODATA) {
1872 log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
1873 return 0;
1874 } else if (r >= 0) {
3b6a3bde 1875 r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &tmp->multipath_routes);
f9bb3338
YW
1876 if (r < 0) {
1877 log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
1878 return 0;
4468f01b 1879 }
4468f01b
YW
1880 }
1881
0b0c81bb
YW
1882 r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
1883 if (r < 0 && r != -ENODATA) {
1884 log_link_warning_errno(link, r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
1885 return 0;
1886 }
1887 has_cacheinfo = r >= 0;
1888
575f14ee
YW
1889 /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
1890 * fib6_nh_init() in net/ipv6/route.c. However, we'd like to manage them by Manager. Hence, set
1891 * link to NULL here. */
1892 if (route_type_is_reject(tmp))
1893 link = NULL;
1894
3b6a3bde 1895 if (!route_needs_convert(tmp))
0b0c81bb 1896 return process_route_one(m, link, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
4468f01b 1897
3b6a3bde
YW
1898 r = route_convert(m, tmp, &converted);
1899 if (r < 0) {
1900 log_link_warning_errno(link, r, "rtnl: failed to convert received route, ignoring: %m");
1901 return 0;
4468f01b
YW
1902 }
1903
3b6a3bde
YW
1904 assert(r > 0);
1905 assert(converted);
1906
1907 for (size_t i = 0; i < converted->n; i++)
0b0c81bb
YW
1908 (void) process_route_one(m,
1909 converted->links[i] ?: link,
1910 type,
1911 TAKE_PTR(converted->routes[i]),
1912 has_cacheinfo ? &cacheinfo : NULL);
3b6a3bde 1913
4468f01b
YW
1914 return 1;
1915}
1916
fa7cd711 1917int network_add_ipv4ll_route(Network *network) {
fcbf4cb7 1918 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 1919 unsigned section_line;
fa7cd711
YW
1920 int r;
1921
1922 assert(network);
1923
1924 if (!network->ipv4ll_route)
1925 return 0;
1926
2a54a044
YW
1927 section_line = hashmap_find_free_section_line(network->routes_by_section);
1928
fa7cd711 1929 /* IPv4LLRoute= is in [Network] section. */
2a54a044 1930 r = route_new_static(network, network->filename, section_line, &n);
fa7cd711
YW
1931 if (r < 0)
1932 return r;
1933
1934 r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
1935 if (r < 0)
1936 return r;
1937
1938 n->family = AF_INET;
1939 n->dst_prefixlen = 16;
1940 n->scope = RT_SCOPE_LINK;
94d6e299
YW
1941 n->scope_set = true;
1942 n->table_set = true;
fa7cd711
YW
1943 n->priority = IPV4LL_ROUTE_METRIC;
1944 n->protocol = RTPROT_STATIC;
1945
1946 TAKE_PTR(n);
1947 return 0;
1948}
1949
5d5003ab
YW
1950int network_add_default_route_on_device(Network *network) {
1951 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 1952 unsigned section_line;
5d5003ab
YW
1953 int r;
1954
1955 assert(network);
1956
1957 if (!network->default_route_on_device)
1958 return 0;
1959
2a54a044
YW
1960 section_line = hashmap_find_free_section_line(network->routes_by_section);
1961
5d5003ab 1962 /* DefaultRouteOnDevice= is in [Network] section. */
2a54a044 1963 r = route_new_static(network, network->filename, section_line, &n);
5d5003ab
YW
1964 if (r < 0)
1965 return r;
1966
5d5003ab 1967 n->family = AF_INET;
c697db75
YW
1968 n->scope = RT_SCOPE_LINK;
1969 n->scope_set = true;
1970 n->protocol = RTPROT_STATIC;
5d5003ab
YW
1971
1972 TAKE_PTR(n);
1973 return 0;
1974}
1975
27efb52b
YW
1976int config_parse_gateway(
1977 const char *unit,
f579559b
TG
1978 const char *filename,
1979 unsigned line,
1980 const char *section,
71a61510 1981 unsigned section_line,
f579559b
TG
1982 const char *lvalue,
1983 int ltype,
1984 const char *rvalue,
1985 void *data,
1986 void *userdata) {
44e7b949 1987
6ae115c1 1988 Network *network = userdata;
fcbf4cb7 1989 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1990 int r;
f579559b
TG
1991
1992 assert(filename);
6ae115c1 1993 assert(section);
f579559b
TG
1994 assert(lvalue);
1995 assert(rvalue);
1996 assert(data);
1997
92fe133a 1998 if (streq(section, "Network")) {
2a54a044
YW
1999 /* we are not in an Route section, so use line number instead */
2000 r = route_new_static(network, filename, line, &n);
d96edb2c
YW
2001 if (r == -ENOMEM)
2002 return log_oom();
2003 if (r < 0) {
2004 log_syntax(unit, LOG_WARNING, filename, line, r,
2005 "Failed to allocate route, ignoring assignment: %m");
2006 return 0;
2007 }
1985c54f 2008 } else {
f4859fc7 2009 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2010 if (r == -ENOMEM)
2011 return log_oom();
2012 if (r < 0) {
2013 log_syntax(unit, LOG_WARNING, filename, line, r,
2014 "Failed to allocate route, ignoring assignment: %m");
2015 return 0;
2016 }
1985c54f 2017
d442bb37 2018 if (isempty(rvalue)) {
1a3a6309 2019 n->gateway_from_dhcp_or_ra = false;
d442bb37
YW
2020 n->gw_family = AF_UNSPEC;
2021 n->gw = IN_ADDR_NULL;
2022 TAKE_PTR(n);
2023 return 0;
2024 }
2025
427928ca 2026 if (streq(rvalue, "_dhcp")) {
1a3a6309 2027 n->gateway_from_dhcp_or_ra = true;
1985c54f
YW
2028 TAKE_PTR(n);
2029 return 0;
2030 }
d306d1d0
YW
2031
2032 if (streq(rvalue, "_dhcp4")) {
2033 n->gw_family = AF_INET;
1a3a6309 2034 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
2035 TAKE_PTR(n);
2036 return 0;
2037 }
2038
b8caa4ef 2039 if (streq(rvalue, "_ipv6ra")) {
d306d1d0 2040 n->gw_family = AF_INET6;
1a3a6309 2041 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
2042 TAKE_PTR(n);
2043 return 0;
2044 }
1985c54f 2045 }
f579559b 2046
6dd53981 2047 r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
f579559b 2048 if (r < 0) {
d96edb2c 2049 log_syntax(unit, LOG_WARNING, filename, line, r,
01d4e732 2050 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
f579559b
TG
2051 return 0;
2052 }
2053
1a3a6309 2054 n->gateway_from_dhcp_or_ra = false;
aff44301 2055 TAKE_PTR(n);
f579559b
TG
2056 return 0;
2057}
6ae115c1 2058
27efb52b
YW
2059int config_parse_preferred_src(
2060 const char *unit,
0d07e595
JK
2061 const char *filename,
2062 unsigned line,
2063 const char *section,
2064 unsigned section_line,
2065 const char *lvalue,
2066 int ltype,
2067 const char *rvalue,
2068 void *data,
2069 void *userdata) {
2070
2071 Network *network = userdata;
fcbf4cb7 2072 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 2073 int r;
0d07e595
JK
2074
2075 assert(filename);
2076 assert(section);
2077 assert(lvalue);
2078 assert(rvalue);
2079 assert(data);
2080
f4859fc7 2081 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2082 if (r == -ENOMEM)
2083 return log_oom();
2084 if (r < 0) {
2085 log_syntax(unit, LOG_WARNING, filename, line, r,
2086 "Failed to allocate route, ignoring assignment: %m");
2087 return 0;
2088 }
0d07e595 2089
01d4e732
YW
2090 if (n->family == AF_UNSPEC)
2091 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
2092 else
2093 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
0d07e595 2094 if (r < 0) {
d96edb2c 2095 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 2096 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
2097 return 0;
2098 }
2099
aff44301 2100 TAKE_PTR(n);
0d07e595
JK
2101 return 0;
2102}
2103
27efb52b
YW
2104int config_parse_destination(
2105 const char *unit,
6ae115c1
TG
2106 const char *filename,
2107 unsigned line,
2108 const char *section,
2109 unsigned section_line,
2110 const char *lvalue,
2111 int ltype,
2112 const char *rvalue,
2113 void *data,
2114 void *userdata) {
44e7b949 2115
6ae115c1 2116 Network *network = userdata;
fcbf4cb7 2117 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede
YW
2118 union in_addr_union *buffer;
2119 unsigned char *prefixlen;
ca3bad65 2120 int r;
6ae115c1
TG
2121
2122 assert(filename);
2123 assert(section);
2124 assert(lvalue);
2125 assert(rvalue);
2126 assert(data);
2127
f4859fc7 2128 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2129 if (r == -ENOMEM)
2130 return log_oom();
2131 if (r < 0) {
2132 log_syntax(unit, LOG_WARNING, filename, line, r,
2133 "Failed to allocate route, ignoring assignment: %m");
2134 return 0;
2135 }
6ae115c1 2136
9e7e4408 2137 if (streq(lvalue, "Destination")) {
7934dede
YW
2138 buffer = &n->dst;
2139 prefixlen = &n->dst_prefixlen;
9e7e4408 2140 } else if (streq(lvalue, "Source")) {
7934dede
YW
2141 buffer = &n->src;
2142 prefixlen = &n->src_prefixlen;
9e7e4408 2143 } else
04499a70 2144 assert_not_reached();
9e7e4408 2145
01d4e732
YW
2146 if (n->family == AF_UNSPEC)
2147 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
2148 else
2149 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede 2150 if (r < 0) {
d96edb2c 2151 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 2152 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
2153 return 0;
2154 }
2155
72fa1923
YW
2156 (void) in_addr_mask(n->family, buffer, *prefixlen);
2157
aff44301 2158 TAKE_PTR(n);
6ae115c1
TG
2159 return 0;
2160}
5d8e593d 2161
27efb52b
YW
2162int config_parse_route_priority(
2163 const char *unit,
2164 const char *filename,
2165 unsigned line,
2166 const char *section,
2167 unsigned section_line,
2168 const char *lvalue,
2169 int ltype,
2170 const char *rvalue,
2171 void *data,
2172 void *userdata) {
2173
5d8e593d 2174 Network *network = userdata;
fcbf4cb7 2175 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
5d8e593d
SS
2176 int r;
2177
2178 assert(filename);
2179 assert(section);
2180 assert(lvalue);
2181 assert(rvalue);
2182 assert(data);
2183
f4859fc7 2184 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2185 if (r == -ENOMEM)
2186 return log_oom();
2187 if (r < 0) {
2188 log_syntax(unit, LOG_WARNING, filename, line, r,
2189 "Failed to allocate route, ignoring assignment: %m");
2190 return 0;
2191 }
5d8e593d 2192
aff44301 2193 r = safe_atou32(rvalue, &n->priority);
1c4b1179 2194 if (r < 0) {
d96edb2c 2195 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
2196 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
2197 return 0;
2198 }
5d8e593d 2199
c27abcf4 2200 n->priority_set = true;
aff44301 2201 TAKE_PTR(n);
5d8e593d
SS
2202 return 0;
2203}
769b56a3 2204
27efb52b
YW
2205int config_parse_route_scope(
2206 const char *unit,
2207 const char *filename,
2208 unsigned line,
2209 const char *section,
2210 unsigned section_line,
2211 const char *lvalue,
2212 int ltype,
2213 const char *rvalue,
2214 void *data,
2215 void *userdata) {
2216
769b56a3 2217 Network *network = userdata;
fcbf4cb7 2218 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
769b56a3
TG
2219 int r;
2220
2221 assert(filename);
2222 assert(section);
2223 assert(lvalue);
2224 assert(rvalue);
2225 assert(data);
2226
f4859fc7 2227 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2228 if (r == -ENOMEM)
2229 return log_oom();
2230 if (r < 0) {
2231 log_syntax(unit, LOG_WARNING, filename, line, r,
2232 "Failed to allocate route, ignoring assignment: %m");
2233 return 0;
2234 }
769b56a3 2235
41b90a1e
YW
2236 r = route_scope_from_string(rvalue);
2237 if (r < 0) {
b98680b2 2238 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown route scope: %s", rvalue);
769b56a3
TG
2239 return 0;
2240 }
2241
41b90a1e 2242 n->scope = r;
94d6e299 2243 n->scope_set = true;
aff44301 2244 TAKE_PTR(n);
769b56a3
TG
2245 return 0;
2246}
c953b24c 2247
324e3422
YW
2248int config_parse_route_nexthop(
2249 const char *unit,
2250 const char *filename,
2251 unsigned line,
2252 const char *section,
2253 unsigned section_line,
2254 const char *lvalue,
2255 int ltype,
2256 const char *rvalue,
2257 void *data,
2258 void *userdata) {
2259
2260 Network *network = userdata;
2261 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2262 uint32_t id;
2263 int r;
2264
2265 assert(filename);
2266 assert(section);
2267 assert(lvalue);
2268 assert(rvalue);
2269 assert(data);
2270
2271 r = route_new_static(network, filename, section_line, &n);
2272 if (r == -ENOMEM)
2273 return log_oom();
2274 if (r < 0) {
2275 log_syntax(unit, LOG_WARNING, filename, line, r,
2276 "Failed to allocate route, ignoring assignment: %m");
2277 return 0;
2278 }
2279
2280 if (isempty(rvalue)) {
2281 n->nexthop_id = 0;
2282 TAKE_PTR(n);
2283 return 0;
2284 }
2285
2286 r = safe_atou32(rvalue, &id);
2287 if (r < 0) {
2288 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue);
2289 return 0;
2290 }
2291 if (id == 0) {
2292 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue);
2293 return 0;
2294 }
2295
2296 n->nexthop_id = id;
2297 TAKE_PTR(n);
2298 return 0;
2299}
2300
27efb52b
YW
2301int config_parse_route_table(
2302 const char *unit,
2303 const char *filename,
2304 unsigned line,
2305 const char *section,
2306 unsigned section_line,
2307 const char *lvalue,
2308 int ltype,
2309 const char *rvalue,
2310 void *data,
2311 void *userdata) {
2312
fcbf4cb7 2313 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c953b24c 2314 Network *network = userdata;
c953b24c
SS
2315 int r;
2316
2317 assert(filename);
2318 assert(section);
2319 assert(lvalue);
2320 assert(rvalue);
2321 assert(data);
2322
f4859fc7 2323 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2324 if (r == -ENOMEM)
2325 return log_oom();
2326 if (r < 0) {
2327 log_syntax(unit, LOG_WARNING, filename, line, r,
2328 "Failed to allocate route, ignoring assignment: %m");
2329 return 0;
2330 }
c953b24c 2331
552b90a2 2332 r = manager_get_route_table_from_string(network->manager, rvalue, &n->table);
c038ce46
SS
2333 if (r < 0) {
2334 log_syntax(unit, LOG_WARNING, filename, line, r,
2335 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
2336 return 0;
c953b24c
SS
2337 }
2338
94d6e299 2339 n->table_set = true;
aff44301 2340 TAKE_PTR(n);
c953b24c
SS
2341 return 0;
2342}
28959f7d 2343
d96edb2c 2344int config_parse_route_boolean(
27efb52b
YW
2345 const char *unit,
2346 const char *filename,
2347 unsigned line,
2348 const char *section,
2349 unsigned section_line,
2350 const char *lvalue,
2351 int ltype,
2352 const char *rvalue,
2353 void *data,
2354 void *userdata) {
2355
28959f7d 2356 Network *network = userdata;
fcbf4cb7 2357 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
28959f7d
SS
2358 int r;
2359
2360 assert(filename);
2361 assert(section);
2362 assert(lvalue);
2363 assert(rvalue);
2364 assert(data);
2365
2366 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2367 if (r == -ENOMEM)
2368 return log_oom();
2369 if (r < 0) {
2370 log_syntax(unit, LOG_WARNING, filename, line, r,
2371 "Failed to allocate route, ignoring assignment: %m");
2372 return 0;
2373 }
28959f7d
SS
2374
2375 r = parse_boolean(rvalue);
2376 if (r < 0) {
d96edb2c 2377 log_syntax(unit, LOG_WARNING, filename, line, r,
9cb8c559 2378 "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
28959f7d
SS
2379 return 0;
2380 }
2381
d96edb2c
YW
2382 if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
2383 n->gateway_onlink = r;
2384 else if (streq(lvalue, "QuickAck"))
2385 n->quickack = r;
2386 else if (streq(lvalue, "FastOpenNoCookie"))
2387 n->fast_open_no_cookie = r;
2388 else if (streq(lvalue, "TTLPropagate"))
2389 n->ttl_propagate = r;
2390 else
04499a70 2391 assert_not_reached();
54901fd2 2392
aff44301 2393 TAKE_PTR(n);
b5bf6f64
SS
2394 return 0;
2395}
2396
27efb52b
YW
2397int config_parse_ipv6_route_preference(
2398 const char *unit,
2399 const char *filename,
2400 unsigned line,
2401 const char *section,
2402 unsigned section_line,
2403 const char *lvalue,
2404 int ltype,
2405 const char *rvalue,
2406 void *data,
2407 void *userdata) {
2408
b5bf6f64 2409 Network *network = userdata;
fcbf4cb7 2410 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
b5bf6f64
SS
2411 int r;
2412
4c7bd9cf 2413 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2414 if (r == -ENOMEM)
2415 return log_oom();
2416 if (r < 0) {
2417 log_syntax(unit, LOG_WARNING, filename, line, r,
2418 "Failed to allocate route, ignoring assignment: %m");
2419 return 0;
2420 }
4c7bd9cf 2421
b5bf6f64
SS
2422 if (streq(rvalue, "low"))
2423 n->pref = ICMPV6_ROUTER_PREF_LOW;
2424 else if (streq(rvalue, "medium"))
2425 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
2426 else if (streq(rvalue, "high"))
2427 n->pref = ICMPV6_ROUTER_PREF_HIGH;
2428 else {
d96edb2c 2429 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
2430 return 0;
2431 }
28959f7d 2432
c27abcf4 2433 n->pref_set = true;
aff44301 2434 TAKE_PTR(n);
28959f7d
SS
2435 return 0;
2436}
c83ecc04 2437
27efb52b
YW
2438int config_parse_route_protocol(
2439 const char *unit,
2440 const char *filename,
2441 unsigned line,
2442 const char *section,
2443 unsigned section_line,
2444 const char *lvalue,
2445 int ltype,
2446 const char *rvalue,
2447 void *data,
2448 void *userdata) {
2449
c83ecc04 2450 Network *network = userdata;
fcbf4cb7 2451 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c83ecc04
SS
2452 int r;
2453
2454 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2455 if (r == -ENOMEM)
2456 return log_oom();
2457 if (r < 0) {
2458 log_syntax(unit, LOG_WARNING, filename, line, r,
2459 "Failed to allocate route, ignoring assignment: %m");
2460 return 0;
2461 }
c83ecc04 2462
1b64651b 2463 r = route_protocol_from_string(rvalue);
e4ffe103
YW
2464 if (r < 0) {
2465 log_syntax(unit, LOG_WARNING, filename, line, r,
2466 "Failed to parse route protocol \"%s\", ignoring assignment: %m", rvalue);
2467 return 0;
c83ecc04
SS
2468 }
2469
e4ffe103
YW
2470 n->protocol = r;
2471
aff44301 2472 TAKE_PTR(n);
c83ecc04
SS
2473 return 0;
2474}
983226f3 2475
27efb52b
YW
2476int config_parse_route_type(
2477 const char *unit,
2478 const char *filename,
2479 unsigned line,
2480 const char *section,
2481 unsigned section_line,
2482 const char *lvalue,
2483 int ltype,
2484 const char *rvalue,
2485 void *data,
2486 void *userdata) {
2487
983226f3 2488 Network *network = userdata;
fcbf4cb7 2489 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7a22312d 2490 int t, r;
983226f3
SS
2491
2492 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2493 if (r == -ENOMEM)
2494 return log_oom();
2495 if (r < 0) {
2496 log_syntax(unit, LOG_WARNING, filename, line, r,
2497 "Failed to allocate route, ignoring assignment: %m");
2498 return 0;
2499 }
983226f3 2500
7a22312d
YW
2501 t = route_type_from_string(rvalue);
2502 if (t < 0) {
b98680b2 2503 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 2504 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
2505 return 0;
2506 }
2507
7a22312d
YW
2508 n->type = (unsigned char) t;
2509
aff44301 2510 TAKE_PTR(n);
983226f3
SS
2511 return 0;
2512}
323d9329 2513
dc7c21f0
YW
2514int config_parse_tcp_congestion(
2515 const char *unit,
2516 const char *filename,
2517 unsigned line,
2518 const char *section,
2519 unsigned section_line,
2520 const char *lvalue,
2521 int ltype,
2522 const char *rvalue,
2523 void *data,
2524 void *userdata) {
2525
2526 Network *network = userdata;
2527 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2528 int r;
2529
2530 assert(filename);
2531 assert(section);
2532 assert(lvalue);
2533 assert(rvalue);
2534 assert(data);
2535
2536 r = route_new_static(network, filename, section_line, &n);
2537 if (r == -ENOMEM)
2538 return log_oom();
2539 if (r < 0) {
2540 log_syntax(unit, LOG_WARNING, filename, line, r,
2541 "Failed to allocate route, ignoring assignment: %m");
2542 return 0;
2543 }
2544
2545 r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
2546 rvalue, &n->tcp_congestion_control_algo, userdata);
2547 if (r < 0)
2548 return r;
2549
2550 TAKE_PTR(n);
2551 return 0;
2552}
2553
007cac09
SS
2554int config_parse_tcp_advmss(
2555 const char *unit,
2556 const char *filename,
2557 unsigned line,
2558 const char *section,
2559 unsigned section_line,
2560 const char *lvalue,
2561 int ltype,
2562 const char *rvalue,
2563 void *data,
2564 void *userdata) {
2565
2566 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2567 Network *network = userdata;
2568 uint64_t u;
2569 int r;
2570
2571 assert(filename);
2572 assert(section);
2573 assert(lvalue);
2574 assert(rvalue);
2575 assert(data);
2576
2577 r = route_new_static(network, filename, section_line, &n);
2578 if (r == -ENOMEM)
2579 return log_oom();
2580 if (r < 0) {
2581 log_syntax(unit, LOG_WARNING, filename, line, r,
2582 "Failed to allocate route, ignoring assignment: %m");
2583 return 0;
2584 }
2585
2586 if (isempty(rvalue)) {
2587 n->advmss = 0;
78ebbf02 2588 TAKE_PTR(n);
007cac09
SS
2589 return 0;
2590 }
2591
2592 r = parse_size(rvalue, 1024, &u);
2593 if (r < 0) {
2594 log_syntax(unit, LOG_WARNING, filename, line, r,
2595 "Could not parse TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2596 return 0;
2597 }
2598
2599 if (u == 0 || u > UINT32_MAX) {
2600 log_syntax(unit, LOG_WARNING, filename, line, 0,
2601 "Invalid TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2602 return 0;
2603 }
2604
2605 n->advmss = u;
2606
2607 TAKE_PTR(n);
2608 return 0;
2609}
2610
27efb52b
YW
2611int config_parse_tcp_window(
2612 const char *unit,
2613 const char *filename,
2614 unsigned line,
2615 const char *section,
2616 unsigned section_line,
2617 const char *lvalue,
2618 int ltype,
2619 const char *rvalue,
2620 void *data,
2621 void *userdata) {
2622
fcbf4cb7 2623 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
6b21ad33 2624 Network *network = userdata;
fef160b5 2625 uint32_t k;
323d9329
SS
2626 int r;
2627
2628 assert(filename);
2629 assert(section);
2630 assert(lvalue);
2631 assert(rvalue);
2632 assert(data);
2633
2634 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2635 if (r == -ENOMEM)
2636 return log_oom();
2637 if (r < 0) {
2638 log_syntax(unit, LOG_WARNING, filename, line, r,
2639 "Failed to allocate route, ignoring assignment: %m");
2640 return 0;
2641 }
323d9329 2642
fef160b5 2643 r = safe_atou32(rvalue, &k);
f205a92a 2644 if (r < 0) {
d96edb2c 2645 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a
YW
2646 "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2647 return 0;
2648 }
fef160b5 2649 if (k >= 1024) {
d96edb2c 2650 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 2651 "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
323d9329
SS
2652 return 0;
2653 }
fd9d7de1
SS
2654 if (k == 0) {
2655 log_syntax(unit, LOG_WARNING, filename, line, 0,
2656 "Invalid TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2657 return 0;
2658 }
323d9329
SS
2659
2660 if (streq(lvalue, "InitialCongestionWindow"))
2661 n->initcwnd = k;
2662 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
2663 n->initrwnd = k;
f205a92a 2664 else
04499a70 2665 assert_not_reached();
323d9329 2666
aff44301 2667 TAKE_PTR(n);
323d9329
SS
2668 return 0;
2669}
09f5dfad 2670
cea79e66
SS
2671int config_parse_route_mtu(
2672 const char *unit,
2673 const char *filename,
2674 unsigned line,
2675 const char *section,
2676 unsigned section_line,
2677 const char *lvalue,
2678 int ltype,
2679 const char *rvalue,
2680 void *data,
2681 void *userdata) {
2682
2683 Network *network = userdata;
fcbf4cb7 2684 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
cea79e66
SS
2685 int r;
2686
2687 assert(filename);
2688 assert(section);
2689 assert(lvalue);
2690 assert(rvalue);
2691 assert(data);
2692
2693 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2694 if (r == -ENOMEM)
2695 return log_oom();
2696 if (r < 0) {
2697 log_syntax(unit, LOG_WARNING, filename, line, r,
2698 "Failed to allocate route, ignoring assignment: %m");
2699 return 0;
2700 }
cea79e66
SS
2701
2702 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
2703 if (r < 0)
2704 return r;
2705
aff44301 2706 TAKE_PTR(n);
cea79e66
SS
2707 return 0;
2708}
fcbf4cb7 2709
6ff5cc6b
YW
2710int config_parse_multipath_route(
2711 const char *unit,
2712 const char *filename,
2713 unsigned line,
2714 const char *section,
2715 unsigned section_line,
2716 const char *lvalue,
2717 int ltype,
2718 const char *rvalue,
2719 void *data,
2720 void *userdata) {
2721
e8f52f3c 2722 _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
6ff5cc6b 2723 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
e8f52f3c 2724 _cleanup_free_ char *word = NULL;
6ff5cc6b 2725 Network *network = userdata;
6ff5cc6b
YW
2726 union in_addr_union a;
2727 int family, r;
e8f52f3c
YW
2728 const char *p;
2729 char *dev;
6ff5cc6b
YW
2730
2731 assert(filename);
2732 assert(section);
2733 assert(lvalue);
2734 assert(rvalue);
2735 assert(data);
2736
2737 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2738 if (r == -ENOMEM)
2739 return log_oom();
2740 if (r < 0) {
2741 log_syntax(unit, LOG_WARNING, filename, line, r,
2742 "Failed to allocate route, ignoring assignment: %m");
2743 return 0;
2744 }
6ff5cc6b
YW
2745
2746 if (isempty(rvalue)) {
e8f52f3c 2747 n->multipath_routes = ordered_set_free_with_destructor(n->multipath_routes, multipath_route_free);
6ff5cc6b
YW
2748 return 0;
2749 }
2750
2751 m = new0(MultipathRoute, 1);
2752 if (!m)
2753 return log_oom();
2754
2755 p = rvalue;
2756 r = extract_first_word(&p, &word, NULL, 0);
2757 if (r == -ENOMEM)
2758 return log_oom();
2759 if (r <= 0) {
d96edb2c 2760 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2761 "Invalid multipath route option, ignoring assignment: %s", rvalue);
2762 return 0;
2763 }
2764
2765 dev = strchr(word, '@');
2766 if (dev) {
e8f52f3c
YW
2767 *dev++ = '\0';
2768
8ed87c49
YW
2769 r = parse_ifindex(dev);
2770 if (r > 0)
2771 m->ifindex = r;
2772 else {
70a2d9dd
YW
2773 if (!ifname_valid_full(dev, IFNAME_VALID_ALTERNATIVE)) {
2774 log_syntax(unit, LOG_WARNING, filename, line, 0,
2775 "Invalid interface name '%s' in %s=, ignoring: %s", dev, lvalue, rvalue);
2776 return 0;
2777 }
2778
8ed87c49
YW
2779 m->ifname = strdup(dev);
2780 if (!m->ifname)
2781 return log_oom();
2782 }
e8f52f3c 2783 }
6ff5cc6b 2784
e8f52f3c 2785 r = in_addr_from_string_auto(word, &family, &a);
6ff5cc6b 2786 if (r < 0) {
d96edb2c 2787 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2788 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
2789 return 0;
2790 }
2791 m->gateway.address = a;
2792 m->gateway.family = family;
2793
6ff5cc6b
YW
2794 if (!isempty(p)) {
2795 r = safe_atou32(p, &m->weight);
2796 if (r < 0) {
d96edb2c 2797 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2798 "Invalid multipath route weight, ignoring assignment: %s", p);
2799 return 0;
2800 }
234106db
YW
2801 /* ip command takes weight in the range 1…255, while kernel takes the value in the
2802 * range 0…254. MultiPathRoute= setting also takes weight in the same range which ip
2803 * command uses, then networkd decreases by one and stores it to match the range which
2804 * kernel uses. */
6ff5cc6b 2805 if (m->weight == 0 || m->weight > 256) {
d96edb2c 2806 log_syntax(unit, LOG_WARNING, filename, line, 0,
6ff5cc6b
YW
2807 "Invalid multipath route weight, ignoring assignment: %s", p);
2808 return 0;
2809 }
234106db 2810 m->weight--;
6ff5cc6b
YW
2811 }
2812
8cb34651
SS
2813 r = ordered_set_ensure_put(&n->multipath_routes, NULL, m);
2814 if (r == -ENOMEM)
6ff5cc6b 2815 return log_oom();
6ff5cc6b 2816 if (r < 0) {
d96edb2c 2817 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2818 "Failed to store multipath route, ignoring assignment: %m");
2819 return 0;
2820 }
2821
2822 TAKE_PTR(m);
2823 TAKE_PTR(n);
2824 return 0;
2825}
2826
d9940a3f 2827static int route_section_verify(Route *route, Network *network) {
fcbf4cb7
YW
2828 if (section_is_invalid(route->section))
2829 return -EINVAL;
2830
3b6a3bde 2831 /* Currently, we do not support static route with finite lifetime. */
91fc5135 2832 assert(route->lifetime_usec == USEC_INFINITY);
3b6a3bde 2833
956dbf36 2834 if (route->gateway_from_dhcp_or_ra) {
c3d679c4
YW
2835 if (route->gw_family == AF_UNSPEC) {
2836 /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
2837 switch (route->family) {
2838 case AF_UNSPEC:
2839 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
2840 "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
2841 route->section->filename, route->section->line);
2842 route->family = AF_INET;
2843 break;
2844 case AF_INET:
2845 case AF_INET6:
2846 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
2847 "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
2848 route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
2849 break;
2850 default:
2851 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2852 "%s: Invalid route family. Ignoring [Route] section from line %u.",
2853 route->section->filename, route->section->line);
2854 }
2855 route->gw_family = route->family;
2856 }
2857
956dbf36
YW
2858 if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
2859 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2860 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
2861 "Ignoring [Route] section from line %u.",
2862 route->section->filename, route->section->line);
2863
2864 if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
2865 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2866 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
2867 "Ignoring [Route] section from line %u.",
2868 route->section->filename, route->section->line);
2869 }
2870
c3d679c4 2871 /* When only Gateway= is specified, assume the route family based on the Gateway address. */
6dd53981
YW
2872 if (route->family == AF_UNSPEC)
2873 route->family = route->gw_family;
2874
fcbf4cb7
YW
2875 if (route->family == AF_UNSPEC) {
2876 assert(route->section);
2877
c3d679c4
YW
2878 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2879 "%s: Route section without Gateway=, Destination=, Source=, "
2880 "or PreferredSource= field configured. "
2881 "Ignoring [Route] section from line %u.",
2882 route->section->filename, route->section->line);
fcbf4cb7
YW
2883 }
2884
6dd53981
YW
2885 if (route->family == AF_INET6 && route->gw_family == AF_INET)
2886 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2887 "%s: IPv4 gateway is configured for IPv6 route. "
2888 "Ignoring [Route] section from line %u.",
2889 route->section->filename, route->section->line);
2890
c0d48bc5
YW
2891 if (!route->table_set && network->vrf) {
2892 route->table = VRF(network->vrf)->table;
2893 route->table_set = true;
2894 }
2895
f5c38922
YW
2896 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
2897 route->table = RT_TABLE_LOCAL;
2898
2899 if (!route->scope_set && route->family != AF_INET6) {
2900 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
2901 route->scope = RT_SCOPE_HOST;
2902 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
2903 route->scope = RT_SCOPE_LINK;
902bbdc4
YW
2904 else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) &&
2905 !route->gateway_from_dhcp_or_ra &&
2906 !in_addr_is_set(route->gw_family, &route->gw) &&
2907 ordered_set_isempty(route->multipath_routes) &&
2908 route->nexthop_id == 0)
2909 route->scope = RT_SCOPE_LINK;
94d6e299
YW
2910 }
2911
fd7701bf
YW
2912 if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
2913 log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
2914 route->scope = RT_SCOPE_UNIVERSE;
2915 }
2916
8973df5c
YW
2917 if (route->family == AF_INET6 && route->priority == 0)
2918 route->priority = IP6_RT_PRIO_USER;
2919
2ddd52d1
YW
2920 if (route->gateway_onlink < 0 && in_addr_is_set(route->gw_family, &route->gw) &&
2921 ordered_hashmap_isempty(network->addresses_by_section)) {
2922 /* If no address is configured, in most cases the gateway cannot be reachable.
2923 * TODO: we may need to improve the condition above. */
fcbf4cb7
YW
2924 log_warning("%s: Gateway= without static address configured. "
2925 "Enabling GatewayOnLink= option.",
2926 network->filename);
2927 route->gateway_onlink = true;
2928 }
2929
17f8d8f9
YW
2930 if (route->gateway_onlink >= 0)
2931 SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
2932
40785f53
YW
2933 if (route->family == AF_INET6) {
2934 MultipathRoute *m;
2935
2936 ORDERED_SET_FOREACH(m, route->multipath_routes)
2937 if (m->gateway.family == AF_INET)
2938 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2939 "%s: IPv4 multipath route is specified for IPv6 route. "
2940 "Ignoring [Route] section from line %u.",
2941 route->section->filename, route->section->line);
2942 }
2943
3c7f1c07
YW
2944 if ((route->gateway_from_dhcp_or_ra ||
2945 in_addr_is_set(route->gw_family, &route->gw)) &&
2946 !ordered_set_isempty(route->multipath_routes))
2947 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2948 "%s: Gateway= cannot be specified with MultiPathRoute=. "
2949 "Ignoring [Route] section from line %u.",
2950 route->section->filename, route->section->line);
2951
324e3422 2952 if (route->nexthop_id > 0 &&
3c7f1c07
YW
2953 (route->gateway_from_dhcp_or_ra ||
2954 in_addr_is_set(route->gw_family, &route->gw) ||
324e3422
YW
2955 !ordered_set_isempty(route->multipath_routes)))
2956 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2957 "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
2958 "Ignoring [Route] section from line %u.",
2959 route->section->filename, route->section->line);
2960
fcbf4cb7
YW
2961 return 0;
2962}
d9940a3f 2963
13ffa39f 2964void network_drop_invalid_routes(Network *network) {
2a54a044 2965 Route *route;
d9940a3f
YW
2966
2967 assert(network);
2968
2a54a044 2969 HASHMAP_FOREACH(route, network->routes_by_section)
d9940a3f
YW
2970 if (route_section_verify(route, network) < 0)
2971 route_free(route);
2972}