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