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