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