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