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