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