]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
network: set key destructor in several hash_ops
[thirdparty/systemd.git] / src / network / networkd-route.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
f579559b 2
b5bf6f64
SS
3#include <linux/icmpv6.h>
4
b5efdb8a 5#include "alloc-util.h"
f579559b 6#include "conf-parser.h"
bb7ae737 7#include "in-addr-util.h"
36dd5ffd 8#include "missing_network.h"
fc2f9534 9#include "netlink-util.h"
ca5ad760 10#include "networkd-ipv4ll.h"
23f53b99 11#include "networkd-manager.h"
6bedfcbb 12#include "networkd-route.h"
6bedfcbb 13#include "parse-util.h"
1c8e710c 14#include "set.h"
d308bb99 15#include "socket-netlink.h"
7a22312d 16#include "string-table.h"
07630cea 17#include "string-util.h"
d96edb2c 18#include "strv.h"
b297e0a7 19#include "strxcpyx.h"
47d2d30d 20#include "sysctl-util.h"
c0d48bc5 21#include "vrf.h"
f579559b 22
47d2d30d
ZJS
23#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
24
25static unsigned routes_max(void) {
26 static thread_local unsigned cached = 0;
27
28 _cleanup_free_ char *s4 = NULL, *s6 = NULL;
29 unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
30
31 if (cached > 0)
32 return cached;
33
34 if (sysctl_read("net/ipv4/route/max_size", &s4) >= 0) {
35 truncate_nl(s4);
36 if (safe_atou(s4, &val4) >= 0 &&
37 val4 == 2147483647U)
38 /* This is the default "no limit" value in the kernel */
39 val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
40 }
41
42 if (sysctl_read("net/ipv6/route/max_size", &s6) >= 0) {
43 truncate_nl(s6);
44 (void) safe_atou(s6, &val6);
45 }
46
47 cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
48 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
49 return cached;
50}
8c34b963 51
ed9e361a 52int route_new(Route **ret) {
8e766630 53 _cleanup_(route_freep) Route *route = NULL;
f0213e37 54
17f9c355 55 route = new(Route, 1);
f0213e37
TG
56 if (!route)
57 return -ENOMEM;
58
17f9c355
YW
59 *route = (Route) {
60 .family = AF_UNSPEC,
61 .scope = RT_SCOPE_UNIVERSE,
62 .protocol = RTPROT_UNSPEC,
63 .type = RTN_UNICAST,
64 .table = RT_TABLE_MAIN,
65 .lifetime = USEC_INFINITY,
66 .quickack = -1,
633c7258 67 .fast_open_no_cookie = -1,
54901fd2 68 .gateway_onlink = -1,
9b88f20a 69 .ttl_propagate = -1,
17f9c355 70 };
f0213e37 71
1cc6c93a 72 *ret = TAKE_PTR(route);
f0213e37
TG
73
74 return 0;
75}
76
9560e5b3 77static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
8e766630
LP
78 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
79 _cleanup_(route_freep) Route *route = NULL;
f0213e37 80 int r;
f579559b 81
8c34b963
LP
82 assert(network);
83 assert(ret);
48317c39 84 assert(!!filename == (section_line > 0));
8c34b963 85
48317c39 86 if (filename) {
f4859fc7
SS
87 r = network_config_section_new(filename, section_line, &n);
88 if (r < 0)
89 return r;
90
91 route = hashmap_get(network->routes_by_section, n);
6ae115c1 92 if (route) {
1cc6c93a 93 *ret = TAKE_PTR(route);
6ae115c1
TG
94
95 return 0;
96 }
97 }
98
47d2d30d 99 if (network->n_static_routes >= routes_max())
8c34b963
LP
100 return -E2BIG;
101
ed9e361a 102 r = route_new(&route);
f0213e37
TG
103 if (r < 0)
104 return r;
801bd9e8 105
ed9e361a 106 route->protocol = RTPROT_STATIC;
0f7f2769
YW
107 route->network = network;
108 LIST_PREPEND(routes, network->static_routes, route);
109 network->n_static_routes++;
f579559b 110
48317c39 111 if (filename) {
1cc6c93a 112 route->section = TAKE_PTR(n);
cacc1dbf 113
3e570042
YW
114 r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
115 if (r < 0)
116 return r;
117
fcc48287 118 r = hashmap_put(network->routes_by_section, route->section, route);
21b39268
LP
119 if (r < 0)
120 return r;
6ae115c1
TG
121 }
122
1cc6c93a 123 *ret = TAKE_PTR(route);
f579559b
TG
124
125 return 0;
126}
127
128void route_free(Route *route) {
129 if (!route)
130 return;
131
f048a16b 132 if (route->network) {
3d3d4255 133 LIST_REMOVE(routes, route->network->static_routes, route);
f579559b 134
8c34b963
LP
135 assert(route->network->n_static_routes > 0);
136 route->network->n_static_routes--;
137
fd45e522 138 if (route->section)
f4859fc7 139 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 140 }
6ae115c1 141
fd45e522
ZJS
142 network_config_section_free(route->section);
143
1c8e710c
TG
144 if (route->link) {
145 set_remove(route->link->routes, route);
146 set_remove(route->link->routes_foreign, route);
147 }
148
6ff5cc6b
YW
149 ordered_set_free_free(route->multipath_routes);
150
f833694d
TG
151 sd_event_source_unref(route->expire);
152
f579559b
TG
153 free(route);
154}
155
7a08d314 156static void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
157 assert(route);
158
159 siphash24_compress(&route->family, sizeof(route->family), state);
160
01aaa3df
YW
161 switch (route->family) {
162 case AF_INET:
163 case AF_INET6:
01aaa3df 164 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
67e05dd8
ZJS
165 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
166
01aaa3df 167 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
67e05dd8
ZJS
168 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
169
170 siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
171
01aaa3df
YW
172 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
173
174 siphash24_compress(&route->tos, sizeof(route->tos), state);
175 siphash24_compress(&route->priority, sizeof(route->priority), state);
176 siphash24_compress(&route->table, sizeof(route->table), state);
177 siphash24_compress(&route->protocol, sizeof(route->protocol), state);
178 siphash24_compress(&route->scope, sizeof(route->scope), state);
179 siphash24_compress(&route->type, sizeof(route->type), state);
67e05dd8 180
fa3e401a
YW
181 siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
182 siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
01aaa3df
YW
183
184 break;
185 default:
186 /* treat any other address family as AF_UNSPEC */
187 break;
188 }
189}
190
c077a205 191static int route_compare_func(const Route *a, const Route *b) {
01aaa3df
YW
192 int r;
193
194 r = CMP(a->family, b->family);
195 if (r != 0)
196 return r;
197
198 switch (a->family) {
199 case AF_INET:
200 case AF_INET6:
201 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
202 if (r != 0)
203 return r;
204
67e05dd8
ZJS
205 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
206 if (r != 0)
207 return r;
208
01aaa3df
YW
209 r = CMP(a->src_prefixlen, b->src_prefixlen);
210 if (r != 0)
211 return r;
212
67e05dd8 213 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
214 if (r != 0)
215 return r;
216
67e05dd8 217 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
218 if (r != 0)
219 return r;
220
67e05dd8 221 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
222 if (r != 0)
223 return r;
224
67e05dd8 225 r = CMP(a->tos, b->tos);
01aaa3df
YW
226 if (r != 0)
227 return r;
228
67e05dd8 229 r = CMP(a->priority, b->priority);
01aaa3df
YW
230 if (r != 0)
231 return r;
232
67e05dd8 233 r = CMP(a->table, b->table);
01aaa3df
YW
234 if (r != 0)
235 return r;
236
67e05dd8 237 r = CMP(a->protocol, b->protocol);
fa3e401a
YW
238 if (r != 0)
239 return r;
240
67e05dd8 241 r = CMP(a->scope, b->scope);
fa3e401a
YW
242 if (r != 0)
243 return r;
244
67e05dd8 245 r = CMP(a->type, b->type);
01aaa3df
YW
246 if (r != 0)
247 return r;
248
67e05dd8 249 r = CMP(a->initcwnd, b->initcwnd);
01aaa3df
YW
250 if (r != 0)
251 return r;
252
67e05dd8 253 r = CMP(a->initrwnd, b->initrwnd);
01aaa3df
YW
254 if (r != 0)
255 return r;
256
67e05dd8 257 return 0;
01aaa3df
YW
258 default:
259 /* treat any other address family as AF_UNSPEC */
260 return 0;
261 }
262}
263
264DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
c077a205 265 route_hash_ops,
01aaa3df 266 Route,
c077a205
YW
267 route_hash_func,
268 route_compare_func,
01aaa3df
YW
269 route_free);
270
7ecf0c3e
TJ
271bool route_equal(Route *r1, Route *r2) {
272 if (r1 == r2)
273 return true;
274
275 if (!r1 || !r2)
276 return false;
277
278 return route_compare_func(r1, r2) == 0;
279}
280
f1368755
YW
281int route_get(Link *link, Route *in, Route **ret) {
282
283 Route *existing;
1b566071
LP
284
285 assert(link);
f1368755 286 assert(in);
1c8e710c 287
f1368755 288 existing = set_get(link->routes, in);
1c8e710c 289 if (existing) {
1b566071
LP
290 if (ret)
291 *ret = existing;
1c8e710c 292 return 1;
1c8e710c
TG
293 }
294
f1368755 295 existing = set_get(link->routes_foreign, in);
1b566071
LP
296 if (existing) {
297 if (ret)
298 *ret = existing;
299 return 0;
300 }
1c8e710c 301
1b566071 302 return -ENOENT;
1c8e710c
TG
303}
304
f1368755 305static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) {
889b550f 306
8e766630 307 _cleanup_(route_freep) Route *route = NULL;
1c8e710c
TG
308 int r;
309
310 assert(link);
311 assert(routes);
f1368755 312 assert(in);
1c8e710c
TG
313
314 r = route_new(&route);
315 if (r < 0)
316 return r;
317
f1368755
YW
318 route->family = in->family;
319 route->src = in->src;
320 route->src_prefixlen = in->src_prefixlen;
321 route->dst = in->dst;
322 route->dst_prefixlen = in->dst_prefixlen;
323 route->gw = in->gw;
324 route->prefsrc = in->prefsrc;
325 route->scope = in->scope;
326 route->protocol = in->protocol;
327 route->type = in->type;
328 route->tos = in->tos;
329 route->priority = in->priority;
330 route->table = in->table;
331 route->initcwnd = in->initcwnd;
332 route->initrwnd = in->initrwnd;
333 route->lifetime = in->lifetime;
1c8e710c 334
de7fef4b 335 r = set_ensure_put(routes, &route_hash_ops, route);
1c8e710c
TG
336 if (r < 0)
337 return r;
75a302b5
YW
338 if (r == 0)
339 return -EEXIST;
1c8e710c
TG
340
341 route->link = link;
342
343 if (ret)
344 *ret = route;
345
346 route = NULL;
347
348 return 0;
349}
350
f1368755
YW
351int route_add_foreign(Link *link, Route *in, Route **ret) {
352 return route_add_internal(link, &link->routes_foreign, in, ret);
1c8e710c
TG
353}
354
f1368755 355int route_add(Link *link, Route *in, Route **ret) {
889b550f 356
1c8e710c
TG
357 Route *route;
358 int r;
359
f1368755 360 r = route_get(link, in, &route);
1c8e710c
TG
361 if (r == -ENOENT) {
362 /* Route does not exist, create a new one */
f1368755 363 r = route_add_internal(link, &link->routes, in, &route);
1c8e710c
TG
364 if (r < 0)
365 return r;
366 } else if (r == 0) {
367 /* Take over a foreign route */
de7fef4b 368 r = set_ensure_put(&link->routes, &route_hash_ops, route);
1c8e710c
TG
369 if (r < 0)
370 return r;
371
372 set_remove(link->routes_foreign, route);
373 } else if (r == 1) {
374 /* Route exists, do nothing */
375 ;
376 } else
377 return r;
378
856e309d
MC
379 if (ret)
380 *ret = route;
1c8e710c
TG
381
382 return 0;
383}
384
302a796f 385static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
386 int r;
387
388 assert(m);
389 assert(link);
390 assert(link->ifname);
391
392 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
393 return 1;
394
395 r = sd_netlink_message_get_errno(m);
396 if (r < 0 && r != -ESRCH)
5ecb131d 397 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
4645ad47
YW
398
399 return 1;
400}
401
91b5f997 402int route_remove(Route *route, Link *link,
302a796f 403 link_netlink_message_handler_t callback) {
27efb52b 404
4afd3348 405 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
406 int r;
407
408 assert(link);
409 assert(link->manager);
410 assert(link->manager->rtnl);
411 assert(link->ifindex > 0);
4c701096 412 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9
UTL
413
414 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
415 RTM_DELROUTE, route->family,
416 route->protocol);
f647962d 417 if (r < 0)
7750b796 418 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 419
b297e0a7
YW
420 if (DEBUG_LOGGING) {
421 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
d3e291fd 422 char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
b297e0a7
YW
423
424 if (!in_addr_is_null(route->family, &route->dst)) {
425 (void) in_addr_to_string(route->family, &route->dst, &dst);
426 (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
427 }
428 if (!in_addr_is_null(route->family, &route->src))
429 (void) in_addr_to_string(route->family, &route->src, &src);
430 if (!in_addr_is_null(route->family, &route->gw))
431 (void) in_addr_to_string(route->family, &route->gw, &gw);
432 if (!in_addr_is_null(route->family, &route->prefsrc))
433 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
434
d3e291fd 435 log_link_debug(link, "Removing route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
b297e0a7
YW
436 strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
437 format_route_scope(route->scope, scope, sizeof(scope)),
438 format_route_table(route->table, table, sizeof(table)),
d3e291fd 439 format_route_protocol(route->protocol, protocol, sizeof(protocol)),
b297e0a7
YW
440 strna(route_type_to_string(route->type)));
441 }
442
d40b01e4 443 if (in_addr_is_null(route->family, &route->gw) == 0) {
43409486 444 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
f647962d 445 if (r < 0)
7750b796 446 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
447 }
448
449 if (route->dst_prefixlen) {
43409486 450 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
f647962d 451 if (r < 0)
7750b796 452 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
453
454 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d 455 if (r < 0)
7750b796 456 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
457 }
458
9e7e4408 459 if (route->src_prefixlen) {
43409486 460 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
9e7e4408 461 if (r < 0)
7750b796 462 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
9e7e4408
TG
463
464 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
465 if (r < 0)
7750b796 466 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
9e7e4408
TG
467 }
468
d40b01e4 469 if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
43409486 470 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
f647962d 471 if (r < 0)
7750b796 472 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
473 }
474
5c1d3fc9 475 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d 476 if (r < 0)
7750b796 477 return log_link_error_errno(link, r, "Could not set scope: %m");
5c1d3fc9 478
86655331 479 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d 480 if (r < 0)
7750b796 481 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 482
2d53f310 483 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
983226f3
SS
484 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
485 if (r < 0)
7750b796 486 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
983226f3 487 }
5c1d3fc9 488
302a796f
YW
489 r = netlink_call_async(link->manager->rtnl, NULL, req,
490 callback ?: route_remove_handler,
491 link_netlink_destroy_callback, link);
f647962d 492 if (r < 0)
7750b796 493 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
5c1d3fc9 494
563c69c6
TG
495 link_ref(link);
496
5c1d3fc9
UTL
497 return 0;
498}
499
f833694d
TG
500int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
501 Route *route = userdata;
502 int r;
503
504 assert(route);
505
4645ad47 506 r = route_remove(route, route->link, NULL);
f833694d 507 if (r < 0)
98b02994 508 log_link_warning_errno(route->link, r, "Could not remove route: %m");
3bdccf69 509 else
fe7ca21a 510 route_free(route);
f833694d
TG
511
512 return 1;
513}
514
6ff5cc6b
YW
515static int append_nexthop_one(Route *route, MultipathRoute *m, struct rtattr **rta, size_t offset) {
516 struct rtnexthop *rtnh;
517 struct rtattr *new_rta;
518 int r;
519
520 assert(route);
521 assert(m);
522 assert(rta);
523 assert(*rta);
524
525 new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
526 if (!new_rta)
527 return -ENOMEM;
528 *rta = new_rta;
529
530 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
531 *rtnh = (struct rtnexthop) {
532 .rtnh_len = sizeof(*rtnh),
533 .rtnh_ifindex = m->ifindex,
534 .rtnh_hops = m->weight > 0 ? m->weight - 1 : 0,
535 };
536
537 (*rta)->rta_len += sizeof(struct rtnexthop);
538
539 if (route->family == m->gateway.family) {
540 r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
541 if (r < 0)
542 goto clear;
543 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
544 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
545 } else {
546 r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
547 if (r < 0)
548 goto clear;
549 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
550 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
551 }
552
553 return 0;
554
555clear:
556 (*rta)->rta_len -= sizeof(struct rtnexthop);
557 return r;
558}
559
560static int append_nexthops(Route *route, sd_netlink_message *req) {
561 _cleanup_free_ struct rtattr *rta = NULL;
562 struct rtnexthop *rtnh;
563 MultipathRoute *m;
564 size_t offset;
565 Iterator i;
566 int r;
567
568 if (ordered_set_isempty(route->multipath_routes))
569 return 0;
570
571 rta = new(struct rtattr, 1);
572 if (!rta)
573 return -ENOMEM;
574
575 *rta = (struct rtattr) {
576 .rta_type = RTA_MULTIPATH,
577 .rta_len = RTA_LENGTH(0),
578 };
579 offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
580
581 ORDERED_SET_FOREACH(m, route->multipath_routes, i) {
582 r = append_nexthop_one(route, m, &rta, offset);
583 if (r < 0)
584 return r;
585
586 rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
587 offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
588 }
589
590 r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
591 if (r < 0)
592 return r;
593
594 return 0;
595}
596
1b566071
LP
597int route_configure(
598 Route *route,
599 Link *link,
302a796f 600 link_netlink_message_handler_t callback) {
1b566071 601
4afd3348
LP
602 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
603 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
f579559b
TG
604 int r;
605
f579559b 606 assert(link);
f882c247
TG
607 assert(link->manager);
608 assert(link->manager->rtnl);
f579559b 609 assert(link->ifindex > 0);
4c701096 610 assert(IN_SET(route->family, AF_INET, AF_INET6));
bd1175bc 611 assert(callback);
f579559b 612
f1368755 613 if (route_get(link, route, NULL) <= 0 &&
47d2d30d 614 set_size(link->routes) >= routes_max())
7750b796
YW
615 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
616 "Too many routes are configured, refusing: %m");
1b566071 617
156ed65e
YW
618 if (DEBUG_LOGGING) {
619 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
d3e291fd 620 char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
156ed65e
YW
621
622 if (!in_addr_is_null(route->family, &route->dst)) {
623 (void) in_addr_to_string(route->family, &route->dst, &dst);
624 (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
625 }
626 if (!in_addr_is_null(route->family, &route->src))
627 (void) in_addr_to_string(route->family, &route->src, &src);
628 if (!in_addr_is_null(route->family, &route->gw))
629 (void) in_addr_to_string(route->family, &route->gw, &gw);
630 if (!in_addr_is_null(route->family, &route->prefsrc))
631 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
632
d3e291fd 633 log_link_debug(link, "Configuring route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
b297e0a7
YW
634 strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
635 format_route_scope(route->scope, scope, sizeof(scope)),
636 format_route_table(route->table, table, sizeof(table)),
d3e291fd 637 format_route_protocol(route->protocol, protocol, sizeof(protocol)),
b297e0a7 638 strna(route_type_to_string(route->type)));
156ed65e
YW
639 }
640
151b9b96 641 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
642 RTM_NEWROUTE, route->family,
643 route->protocol);
f647962d 644 if (r < 0)
7750b796 645 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
f579559b 646
d40b01e4 647 if (in_addr_is_null(route->family, &route->gw) == 0) {
43409486 648 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
f647962d 649 if (r < 0)
7750b796 650 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
c953b24c
SS
651
652 r = sd_rtnl_message_route_set_family(req, route->family);
653 if (r < 0)
7750b796 654 return log_link_error_errno(link, r, "Could not set route family: %m");
f579559b
TG
655 }
656
70dc2362 657 if (route->dst_prefixlen > 0) {
43409486 658 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
f647962d 659 if (r < 0)
7750b796 660 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
6ae115c1 661
ae4c67a7 662 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d 663 if (r < 0)
7750b796 664 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
1f01fb4f
TG
665 }
666
70dc2362 667 if (route->src_prefixlen > 0) {
43409486 668 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
9e7e4408 669 if (r < 0)
7750b796 670 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
9e7e4408
TG
671
672 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
673 if (r < 0)
7750b796 674 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
9e7e4408
TG
675 }
676
d40b01e4 677 if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
43409486 678 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
f647962d 679 if (r < 0)
7750b796 680 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
681 }
682
5c1d3fc9 683 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d 684 if (r < 0)
7750b796 685 return log_link_error_errno(link, r, "Could not set scope: %m");
5c1d3fc9 686
54901fd2
YW
687 if (route->gateway_onlink >= 0)
688 SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
689
3b015d40
TG
690 r = sd_rtnl_message_route_set_flags(req, route->flags);
691 if (r < 0)
7750b796 692 return log_link_error_errno(link, r, "Could not set flags: %m");
c953b24c 693
a0d95bbc 694 if (route->table != RT_TABLE_MAIN) {
c953b24c
SS
695 if (route->table < 256) {
696 r = sd_rtnl_message_route_set_table(req, route->table);
697 if (r < 0)
7750b796 698 return log_link_error_errno(link, r, "Could not set route table: %m");
c953b24c 699 } else {
c953b24c
SS
700 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
701 if (r < 0)
7750b796 702 return log_link_error_errno(link, r, "Could not set route table: %m");
c953b24c 703
06976f5b 704 /* Table attribute to allow more than 256. */
c953b24c
SS
705 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
706 if (r < 0)
7750b796 707 return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
c953b24c
SS
708 }
709 }
3b015d40 710
86655331 711 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d 712 if (r < 0)
7750b796 713 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 714
3b015d40
TG
715 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
716 if (r < 0)
7750b796 717 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
3b015d40 718
f02ba163
DD
719 if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
720 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
721 DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
722 if (r < 0)
7750b796 723 return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
f02ba163
DD
724 }
725
983226f3 726 r = sd_rtnl_message_route_set_type(req, route->type);
f647962d 727 if (r < 0)
7750b796 728 return log_link_error_errno(link, r, "Could not set route type: %m");
983226f3 729
2d53f310 730 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
983226f3
SS
731 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
732 if (r < 0)
7750b796 733 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
983226f3 734 }
f579559b 735
9b88f20a
SS
736 if (route->ttl_propagate >= 0) {
737 r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
738 if (r < 0)
739 return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
740 }
741
d6fceaf1
SS
742 r = sd_netlink_message_open_container(req, RTA_METRICS);
743 if (r < 0)
7750b796 744 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1
SS
745
746 if (route->mtu > 0) {
747 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
748 if (r < 0)
7750b796 749 return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
d6fceaf1
SS
750 }
751
6b21ad33 752 if (route->initcwnd > 0) {
323d9329
SS
753 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
754 if (r < 0)
7750b796 755 return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
323d9329
SS
756 }
757
6b21ad33 758 if (route->initrwnd > 0) {
323d9329
SS
759 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
760 if (r < 0)
7750b796 761 return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
323d9329
SS
762 }
763
67c193bf 764 if (route->quickack >= 0) {
09f5dfad
SS
765 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
766 if (r < 0)
7750b796 767 return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
09f5dfad
SS
768 }
769
633c7258
SS
770 if (route->fast_open_no_cookie >= 0) {
771 r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
772 if (r < 0)
773 return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
774 }
775
d6fceaf1
SS
776 r = sd_netlink_message_close_container(req);
777 if (r < 0)
7750b796 778 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1 779
6ff5cc6b
YW
780 r = append_nexthops(route, req);
781 if (r < 0)
782 return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
783
302a796f
YW
784 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
785 link_netlink_destroy_callback, link);
f647962d 786 if (r < 0)
7750b796 787 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
f579559b 788
563c69c6
TG
789 link_ref(link);
790
f1368755 791 r = route_add(link, route, &route);
1c8e710c 792 if (r < 0)
7750b796 793 return log_link_error_errno(link, r, "Could not add route: %m");
1c8e710c 794
f833694d 795 /* TODO: drop expiration handling once it can be pushed into the kernel */
f02ba163 796 if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
f833694d
TG
797 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
798 route->lifetime, 0, route_expire_handler, route);
799 if (r < 0)
7750b796 800 return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
f833694d
TG
801 }
802
803 sd_event_source_unref(route->expire);
1cc6c93a 804 route->expire = TAKE_PTR(expire);
f833694d 805
c4423317 806 return 1;
f579559b
TG
807}
808
fa7cd711 809int network_add_ipv4ll_route(Network *network) {
fcbf4cb7 810 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
fa7cd711
YW
811 int r;
812
813 assert(network);
814
815 if (!network->ipv4ll_route)
816 return 0;
817
818 /* IPv4LLRoute= is in [Network] section. */
819 r = route_new_static(network, NULL, 0, &n);
820 if (r < 0)
821 return r;
822
823 r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
824 if (r < 0)
825 return r;
826
827 n->family = AF_INET;
828 n->dst_prefixlen = 16;
829 n->scope = RT_SCOPE_LINK;
94d6e299
YW
830 n->scope_set = true;
831 n->table_set = true;
fa7cd711
YW
832 n->priority = IPV4LL_ROUTE_METRIC;
833 n->protocol = RTPROT_STATIC;
834
835 TAKE_PTR(n);
836 return 0;
837}
838
5d5003ab
YW
839int network_add_default_route_on_device(Network *network) {
840 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
841 int r;
842
843 assert(network);
844
845 if (!network->default_route_on_device)
846 return 0;
847
848 /* DefaultRouteOnDevice= is in [Network] section. */
849 r = route_new_static(network, NULL, 0, &n);
850 if (r < 0)
851 return r;
852
5d5003ab 853 n->family = AF_INET;
c697db75
YW
854 n->scope = RT_SCOPE_LINK;
855 n->scope_set = true;
856 n->protocol = RTPROT_STATIC;
5d5003ab
YW
857
858 TAKE_PTR(n);
859 return 0;
860}
861
7a22312d
YW
862static const char * const route_type_table[__RTN_MAX] = {
863 [RTN_UNICAST] = "unicast",
94d6e299
YW
864 [RTN_LOCAL] = "local",
865 [RTN_BROADCAST] = "broadcast",
866 [RTN_ANYCAST] = "anycast",
867 [RTN_MULTICAST] = "multicast",
7a22312d
YW
868 [RTN_BLACKHOLE] = "blackhole",
869 [RTN_UNREACHABLE] = "unreachable",
870 [RTN_PROHIBIT] = "prohibit",
871 [RTN_THROW] = "throw",
94d6e299
YW
872 [RTN_NAT] = "nat",
873 [RTN_XRESOLVE] = "xresolve",
7a22312d
YW
874};
875
876assert_cc(__RTN_MAX <= UCHAR_MAX);
877DEFINE_STRING_TABLE_LOOKUP(route_type, int);
878
b297e0a7
YW
879static const char * const route_scope_table[] = {
880 [RT_SCOPE_UNIVERSE] = "global",
881 [RT_SCOPE_SITE] = "site",
882 [RT_SCOPE_LINK] = "link",
883 [RT_SCOPE_HOST] = "host",
884 [RT_SCOPE_NOWHERE] = "nowhere",
885};
886
41b90a1e 887DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
b297e0a7
YW
888
889const char *format_route_scope(int scope, char *buf, size_t size) {
890 const char *s;
891 char *p = buf;
892
893 s = route_scope_to_string(scope);
894 if (s)
895 strpcpy(&p, size, s);
896 else
897 strpcpyf(&p, size, "%d", scope);
898
899 return buf;
900}
901
902static const char * const route_table_table[] = {
903 [RT_TABLE_DEFAULT] = "default",
904 [RT_TABLE_MAIN] = "main",
905 [RT_TABLE_LOCAL] = "local",
906};
907
41b90a1e 908DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
b297e0a7
YW
909
910const char *format_route_table(int table, char *buf, size_t size) {
911 const char *s;
912 char *p = buf;
913
914 s = route_table_to_string(table);
915 if (s)
916 strpcpy(&p, size, s);
917 else
918 strpcpyf(&p, size, "%d", table);
919
920 return buf;
921}
922
1b64651b
YW
923static const char * const route_protocol_table[] = {
924 [RTPROT_KERNEL] = "kernel",
925 [RTPROT_BOOT] = "boot",
926 [RTPROT_STATIC] = "static",
927};
928
ca420b62
YW
929DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int);
930
931static const char * const route_protocol_full_table[] = {
932 [RTPROT_REDIRECT] = "redirect",
933 [RTPROT_KERNEL] = "kernel",
934 [RTPROT_BOOT] = "boot",
935 [RTPROT_STATIC] = "static",
936 [RTPROT_GATED] = "gated",
937 [RTPROT_RA] = "ra",
938 [RTPROT_MRT] = "mrt",
939 [RTPROT_ZEBRA] = "zebra",
940 [RTPROT_BIRD] = "bird",
941 [RTPROT_DNROUTED] = "dnrouted",
942 [RTPROT_XORP] = "xorp",
943 [RTPROT_NTK] = "ntk",
944 [RTPROT_DHCP] = "dhcp",
945 [RTPROT_MROUTED] = "mrouted",
946 [RTPROT_BABEL] = "babel",
947 [RTPROT_BGP] = "bgp",
948 [RTPROT_ISIS] = "isis",
949 [RTPROT_OSPF] = "ospf",
950 [RTPROT_RIP] = "rip",
951 [RTPROT_EIGRP] = "eigrp",
952};
953
954DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int);
d3e291fd
YW
955
956const char *format_route_protocol(int protocol, char *buf, size_t size) {
957 const char *s;
958 char *p = buf;
959
ca420b62 960 s = route_protocol_full_to_string(protocol);
d3e291fd
YW
961 if (s)
962 strpcpy(&p, size, s);
963 else
964 strpcpyf(&p, size, "%d", protocol);
965
966 return buf;
967}
1b64651b 968
27efb52b
YW
969int config_parse_gateway(
970 const char *unit,
f579559b
TG
971 const char *filename,
972 unsigned line,
973 const char *section,
71a61510 974 unsigned section_line,
f579559b
TG
975 const char *lvalue,
976 int ltype,
977 const char *rvalue,
978 void *data,
979 void *userdata) {
44e7b949 980
6ae115c1 981 Network *network = userdata;
fcbf4cb7 982 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 983 int r;
f579559b
TG
984
985 assert(filename);
6ae115c1 986 assert(section);
f579559b
TG
987 assert(lvalue);
988 assert(rvalue);
989 assert(data);
990
92fe133a
TG
991 if (streq(section, "Network")) {
992 /* we are not in an Route section, so treat
993 * this as the special '0' section */
f4859fc7 994 r = route_new_static(network, NULL, 0, &n);
d96edb2c
YW
995 if (r == -ENOMEM)
996 return log_oom();
997 if (r < 0) {
998 log_syntax(unit, LOG_WARNING, filename, line, r,
999 "Failed to allocate route, ignoring assignment: %m");
1000 return 0;
1001 }
1985c54f 1002 } else {
f4859fc7 1003 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1004 if (r == -ENOMEM)
1005 return log_oom();
1006 if (r < 0) {
1007 log_syntax(unit, LOG_WARNING, filename, line, r,
1008 "Failed to allocate route, ignoring assignment: %m");
1009 return 0;
1010 }
1985c54f 1011
427928ca 1012 if (streq(rvalue, "_dhcp")) {
1985c54f
YW
1013 n->gateway_from_dhcp = true;
1014 TAKE_PTR(n);
1015 return 0;
1016 }
1017 }
f579559b 1018
01d4e732
YW
1019 if (n->family == AF_UNSPEC)
1020 r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
1021 else
1022 r = in_addr_from_string(n->family, rvalue, &n->gw);
f579559b 1023 if (r < 0) {
d96edb2c 1024 log_syntax(unit, LOG_WARNING, filename, line, r,
01d4e732 1025 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
f579559b
TG
1026 return 0;
1027 }
1028
aff44301 1029 TAKE_PTR(n);
f579559b
TG
1030 return 0;
1031}
6ae115c1 1032
27efb52b
YW
1033int config_parse_preferred_src(
1034 const char *unit,
0d07e595
JK
1035 const char *filename,
1036 unsigned line,
1037 const char *section,
1038 unsigned section_line,
1039 const char *lvalue,
1040 int ltype,
1041 const char *rvalue,
1042 void *data,
1043 void *userdata) {
1044
1045 Network *network = userdata;
fcbf4cb7 1046 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1047 int r;
0d07e595
JK
1048
1049 assert(filename);
1050 assert(section);
1051 assert(lvalue);
1052 assert(rvalue);
1053 assert(data);
1054
f4859fc7 1055 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1056 if (r == -ENOMEM)
1057 return log_oom();
1058 if (r < 0) {
1059 log_syntax(unit, LOG_WARNING, filename, line, r,
1060 "Failed to allocate route, ignoring assignment: %m");
1061 return 0;
1062 }
0d07e595 1063
01d4e732
YW
1064 if (n->family == AF_UNSPEC)
1065 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
1066 else
1067 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
0d07e595 1068 if (r < 0) {
d96edb2c 1069 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1070 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
1071 return 0;
1072 }
1073
aff44301 1074 TAKE_PTR(n);
0d07e595
JK
1075 return 0;
1076}
1077
27efb52b
YW
1078int config_parse_destination(
1079 const char *unit,
6ae115c1
TG
1080 const char *filename,
1081 unsigned line,
1082 const char *section,
1083 unsigned section_line,
1084 const char *lvalue,
1085 int ltype,
1086 const char *rvalue,
1087 void *data,
1088 void *userdata) {
44e7b949 1089
6ae115c1 1090 Network *network = userdata;
fcbf4cb7 1091 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede
YW
1092 union in_addr_union *buffer;
1093 unsigned char *prefixlen;
ca3bad65 1094 int r;
6ae115c1
TG
1095
1096 assert(filename);
1097 assert(section);
1098 assert(lvalue);
1099 assert(rvalue);
1100 assert(data);
1101
f4859fc7 1102 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1103 if (r == -ENOMEM)
1104 return log_oom();
1105 if (r < 0) {
1106 log_syntax(unit, LOG_WARNING, filename, line, r,
1107 "Failed to allocate route, ignoring assignment: %m");
1108 return 0;
1109 }
6ae115c1 1110
9e7e4408 1111 if (streq(lvalue, "Destination")) {
7934dede
YW
1112 buffer = &n->dst;
1113 prefixlen = &n->dst_prefixlen;
9e7e4408 1114 } else if (streq(lvalue, "Source")) {
7934dede
YW
1115 buffer = &n->src;
1116 prefixlen = &n->src_prefixlen;
9e7e4408
TG
1117 } else
1118 assert_not_reached(lvalue);
1119
01d4e732
YW
1120 if (n->family == AF_UNSPEC)
1121 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
1122 else
1123 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede 1124 if (r < 0) {
d96edb2c 1125 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1126 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
1127 return 0;
1128 }
1129
aff44301 1130 TAKE_PTR(n);
6ae115c1
TG
1131 return 0;
1132}
5d8e593d 1133
27efb52b
YW
1134int config_parse_route_priority(
1135 const char *unit,
1136 const char *filename,
1137 unsigned line,
1138 const char *section,
1139 unsigned section_line,
1140 const char *lvalue,
1141 int ltype,
1142 const char *rvalue,
1143 void *data,
1144 void *userdata) {
1145
5d8e593d 1146 Network *network = userdata;
fcbf4cb7 1147 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
5d8e593d
SS
1148 int r;
1149
1150 assert(filename);
1151 assert(section);
1152 assert(lvalue);
1153 assert(rvalue);
1154 assert(data);
1155
f4859fc7 1156 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1157 if (r == -ENOMEM)
1158 return log_oom();
1159 if (r < 0) {
1160 log_syntax(unit, LOG_WARNING, filename, line, r,
1161 "Failed to allocate route, ignoring assignment: %m");
1162 return 0;
1163 }
5d8e593d 1164
aff44301 1165 r = safe_atou32(rvalue, &n->priority);
1c4b1179 1166 if (r < 0) {
d96edb2c 1167 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
1168 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1169 return 0;
1170 }
5d8e593d 1171
aff44301 1172 TAKE_PTR(n);
5d8e593d
SS
1173 return 0;
1174}
769b56a3 1175
27efb52b
YW
1176int config_parse_route_scope(
1177 const char *unit,
1178 const char *filename,
1179 unsigned line,
1180 const char *section,
1181 unsigned section_line,
1182 const char *lvalue,
1183 int ltype,
1184 const char *rvalue,
1185 void *data,
1186 void *userdata) {
1187
769b56a3 1188 Network *network = userdata;
fcbf4cb7 1189 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
769b56a3
TG
1190 int r;
1191
1192 assert(filename);
1193 assert(section);
1194 assert(lvalue);
1195 assert(rvalue);
1196 assert(data);
1197
f4859fc7 1198 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1199 if (r == -ENOMEM)
1200 return log_oom();
1201 if (r < 0) {
1202 log_syntax(unit, LOG_WARNING, filename, line, r,
1203 "Failed to allocate route, ignoring assignment: %m");
1204 return 0;
1205 }
769b56a3 1206
41b90a1e
YW
1207 r = route_scope_from_string(rvalue);
1208 if (r < 0) {
d96edb2c 1209 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
1210 return 0;
1211 }
1212
41b90a1e 1213 n->scope = r;
94d6e299 1214 n->scope_set = true;
aff44301 1215 TAKE_PTR(n);
769b56a3
TG
1216 return 0;
1217}
c953b24c 1218
27efb52b
YW
1219int config_parse_route_table(
1220 const char *unit,
1221 const char *filename,
1222 unsigned line,
1223 const char *section,
1224 unsigned section_line,
1225 const char *lvalue,
1226 int ltype,
1227 const char *rvalue,
1228 void *data,
1229 void *userdata) {
1230
fcbf4cb7 1231 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c953b24c 1232 Network *network = userdata;
c953b24c
SS
1233 int r;
1234
1235 assert(filename);
1236 assert(section);
1237 assert(lvalue);
1238 assert(rvalue);
1239 assert(data);
1240
f4859fc7 1241 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1242 if (r == -ENOMEM)
1243 return log_oom();
1244 if (r < 0) {
1245 log_syntax(unit, LOG_WARNING, filename, line, r,
1246 "Failed to allocate route, ignoring assignment: %m");
1247 return 0;
1248 }
c953b24c 1249
41b90a1e
YW
1250 r = route_table_from_string(rvalue);
1251 if (r >= 0)
1252 n->table = r;
1253 else {
1254 r = safe_atou32(rvalue, &n->table);
1255 if (r < 0) {
d96edb2c 1256 log_syntax(unit, LOG_WARNING, filename, line, r,
41b90a1e
YW
1257 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
1258 return 0;
1259 }
c953b24c
SS
1260 }
1261
94d6e299 1262 n->table_set = true;
aff44301 1263 TAKE_PTR(n);
c953b24c
SS
1264 return 0;
1265}
28959f7d 1266
d96edb2c 1267int config_parse_route_boolean(
27efb52b
YW
1268 const char *unit,
1269 const char *filename,
1270 unsigned line,
1271 const char *section,
1272 unsigned section_line,
1273 const char *lvalue,
1274 int ltype,
1275 const char *rvalue,
1276 void *data,
1277 void *userdata) {
1278
28959f7d 1279 Network *network = userdata;
fcbf4cb7 1280 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
28959f7d
SS
1281 int r;
1282
1283 assert(filename);
1284 assert(section);
1285 assert(lvalue);
1286 assert(rvalue);
1287 assert(data);
1288
1289 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1290 if (r == -ENOMEM)
1291 return log_oom();
1292 if (r < 0) {
1293 log_syntax(unit, LOG_WARNING, filename, line, r,
1294 "Failed to allocate route, ignoring assignment: %m");
1295 return 0;
1296 }
28959f7d
SS
1297
1298 r = parse_boolean(rvalue);
1299 if (r < 0) {
d96edb2c 1300 log_syntax(unit, LOG_WARNING, filename, line, r,
9cb8c559 1301 "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
28959f7d
SS
1302 return 0;
1303 }
1304
d96edb2c
YW
1305 if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
1306 n->gateway_onlink = r;
1307 else if (streq(lvalue, "QuickAck"))
1308 n->quickack = r;
1309 else if (streq(lvalue, "FastOpenNoCookie"))
1310 n->fast_open_no_cookie = r;
1311 else if (streq(lvalue, "TTLPropagate"))
1312 n->ttl_propagate = r;
1313 else
1314 assert_not_reached("Invalid lvalue");
54901fd2 1315
aff44301 1316 TAKE_PTR(n);
b5bf6f64
SS
1317 return 0;
1318}
1319
27efb52b
YW
1320int config_parse_ipv6_route_preference(
1321 const char *unit,
1322 const char *filename,
1323 unsigned line,
1324 const char *section,
1325 unsigned section_line,
1326 const char *lvalue,
1327 int ltype,
1328 const char *rvalue,
1329 void *data,
1330 void *userdata) {
1331
b5bf6f64 1332 Network *network = userdata;
fcbf4cb7 1333 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
b5bf6f64
SS
1334 int r;
1335
4c7bd9cf 1336 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1337 if (r == -ENOMEM)
1338 return log_oom();
1339 if (r < 0) {
1340 log_syntax(unit, LOG_WARNING, filename, line, r,
1341 "Failed to allocate route, ignoring assignment: %m");
1342 return 0;
1343 }
4c7bd9cf 1344
b5bf6f64
SS
1345 if (streq(rvalue, "low"))
1346 n->pref = ICMPV6_ROUTER_PREF_LOW;
1347 else if (streq(rvalue, "medium"))
1348 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
1349 else if (streq(rvalue, "high"))
1350 n->pref = ICMPV6_ROUTER_PREF_HIGH;
1351 else {
d96edb2c 1352 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
1353 return 0;
1354 }
28959f7d 1355
aff44301 1356 TAKE_PTR(n);
28959f7d
SS
1357 return 0;
1358}
c83ecc04 1359
27efb52b
YW
1360int config_parse_route_protocol(
1361 const char *unit,
1362 const char *filename,
1363 unsigned line,
1364 const char *section,
1365 unsigned section_line,
1366 const char *lvalue,
1367 int ltype,
1368 const char *rvalue,
1369 void *data,
1370 void *userdata) {
1371
c83ecc04 1372 Network *network = userdata;
fcbf4cb7 1373 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c83ecc04
SS
1374 int r;
1375
1376 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1377 if (r == -ENOMEM)
1378 return log_oom();
1379 if (r < 0) {
1380 log_syntax(unit, LOG_WARNING, filename, line, r,
1381 "Failed to allocate route, ignoring assignment: %m");
1382 return 0;
1383 }
c83ecc04 1384
1b64651b
YW
1385 r = route_protocol_from_string(rvalue);
1386 if (r >= 0)
1387 n->protocol = r;
c83ecc04
SS
1388 else {
1389 r = safe_atou8(rvalue , &n->protocol);
1390 if (r < 0) {
d96edb2c 1391 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 1392 "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
c83ecc04
SS
1393 return 0;
1394 }
1395 }
1396
aff44301 1397 TAKE_PTR(n);
c83ecc04
SS
1398 return 0;
1399}
983226f3 1400
27efb52b
YW
1401int config_parse_route_type(
1402 const char *unit,
1403 const char *filename,
1404 unsigned line,
1405 const char *section,
1406 unsigned section_line,
1407 const char *lvalue,
1408 int ltype,
1409 const char *rvalue,
1410 void *data,
1411 void *userdata) {
1412
983226f3 1413 Network *network = userdata;
fcbf4cb7 1414 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7a22312d 1415 int t, r;
983226f3
SS
1416
1417 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1418 if (r == -ENOMEM)
1419 return log_oom();
1420 if (r < 0) {
1421 log_syntax(unit, LOG_WARNING, filename, line, r,
1422 "Failed to allocate route, ignoring assignment: %m");
1423 return 0;
1424 }
983226f3 1425
7a22312d
YW
1426 t = route_type_from_string(rvalue);
1427 if (t < 0) {
d96edb2c 1428 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 1429 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
1430 return 0;
1431 }
1432
7a22312d
YW
1433 n->type = (unsigned char) t;
1434
aff44301 1435 TAKE_PTR(n);
983226f3
SS
1436 return 0;
1437}
323d9329 1438
27efb52b
YW
1439int config_parse_tcp_window(
1440 const char *unit,
1441 const char *filename,
1442 unsigned line,
1443 const char *section,
1444 unsigned section_line,
1445 const char *lvalue,
1446 int ltype,
1447 const char *rvalue,
1448 void *data,
1449 void *userdata) {
1450
fcbf4cb7 1451 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
6b21ad33
SS
1452 Network *network = userdata;
1453 uint64_t k;
323d9329
SS
1454 int r;
1455
1456 assert(filename);
1457 assert(section);
1458 assert(lvalue);
1459 assert(rvalue);
1460 assert(data);
1461
1462 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1463 if (r == -ENOMEM)
1464 return log_oom();
1465 if (r < 0) {
1466 log_syntax(unit, LOG_WARNING, filename, line, r,
1467 "Failed to allocate route, ignoring assignment: %m");
1468 return 0;
1469 }
323d9329 1470
6b21ad33 1471 r = parse_size(rvalue, 1024, &k);
f205a92a 1472 if (r < 0) {
d96edb2c 1473 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a
YW
1474 "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
1475 return 0;
1476 }
1477 if (k > UINT32_MAX) {
d96edb2c 1478 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 1479 "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
323d9329
SS
1480 return 0;
1481 }
1482
1483 if (streq(lvalue, "InitialCongestionWindow"))
1484 n->initcwnd = k;
1485 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
1486 n->initrwnd = k;
f205a92a
YW
1487 else
1488 assert_not_reached("Invalid TCP window type.");
323d9329 1489
aff44301 1490 TAKE_PTR(n);
323d9329
SS
1491 return 0;
1492}
09f5dfad 1493
cea79e66
SS
1494int config_parse_route_mtu(
1495 const char *unit,
1496 const char *filename,
1497 unsigned line,
1498 const char *section,
1499 unsigned section_line,
1500 const char *lvalue,
1501 int ltype,
1502 const char *rvalue,
1503 void *data,
1504 void *userdata) {
1505
1506 Network *network = userdata;
fcbf4cb7 1507 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
cea79e66
SS
1508 int r;
1509
1510 assert(filename);
1511 assert(section);
1512 assert(lvalue);
1513 assert(rvalue);
1514 assert(data);
1515
1516 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1517 if (r == -ENOMEM)
1518 return log_oom();
1519 if (r < 0) {
1520 log_syntax(unit, LOG_WARNING, filename, line, r,
1521 "Failed to allocate route, ignoring assignment: %m");
1522 return 0;
1523 }
cea79e66
SS
1524
1525 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
1526 if (r < 0)
1527 return r;
1528
aff44301 1529 TAKE_PTR(n);
cea79e66
SS
1530 return 0;
1531}
fcbf4cb7 1532
6ff5cc6b
YW
1533int config_parse_multipath_route(
1534 const char *unit,
1535 const char *filename,
1536 unsigned line,
1537 const char *section,
1538 unsigned section_line,
1539 const char *lvalue,
1540 int ltype,
1541 const char *rvalue,
1542 void *data,
1543 void *userdata) {
1544
1545 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
1546 _cleanup_free_ char *word = NULL, *buf = NULL;
1547 _cleanup_free_ MultipathRoute *m = NULL;
1548 Network *network = userdata;
1549 const char *p, *ip, *dev;
1550 union in_addr_union a;
1551 int family, r;
1552
1553 assert(filename);
1554 assert(section);
1555 assert(lvalue);
1556 assert(rvalue);
1557 assert(data);
1558
1559 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1560 if (r == -ENOMEM)
1561 return log_oom();
1562 if (r < 0) {
1563 log_syntax(unit, LOG_WARNING, filename, line, r,
1564 "Failed to allocate route, ignoring assignment: %m");
1565 return 0;
1566 }
6ff5cc6b
YW
1567
1568 if (isempty(rvalue)) {
1569 n->multipath_routes = ordered_set_free_free(n->multipath_routes);
1570 return 0;
1571 }
1572
1573 m = new0(MultipathRoute, 1);
1574 if (!m)
1575 return log_oom();
1576
1577 p = rvalue;
1578 r = extract_first_word(&p, &word, NULL, 0);
1579 if (r == -ENOMEM)
1580 return log_oom();
1581 if (r <= 0) {
d96edb2c 1582 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1583 "Invalid multipath route option, ignoring assignment: %s", rvalue);
1584 return 0;
1585 }
1586
1587 dev = strchr(word, '@');
1588 if (dev) {
1589 buf = strndup(word, dev - word);
1590 if (!buf)
1591 return log_oom();
1592 ip = buf;
1593 dev++;
1594 } else
1595 ip = word;
1596
1597 r = in_addr_from_string_auto(ip, &family, &a);
1598 if (r < 0) {
d96edb2c 1599 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1600 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
1601 return 0;
1602 }
1603 m->gateway.address = a;
1604 m->gateway.family = family;
1605
1606 if (dev) {
d308bb99 1607 r = resolve_interface(NULL, dev);
6ff5cc6b 1608 if (r < 0) {
d96edb2c 1609 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1610 "Invalid interface name or index, ignoring assignment: %s", dev);
1611 return 0;
1612 }
597da51b 1613 m->ifindex = r;
6ff5cc6b
YW
1614 }
1615
1616 if (!isempty(p)) {
1617 r = safe_atou32(p, &m->weight);
1618 if (r < 0) {
d96edb2c 1619 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1620 "Invalid multipath route weight, ignoring assignment: %s", p);
1621 return 0;
1622 }
1623 if (m->weight == 0 || m->weight > 256) {
d96edb2c 1624 log_syntax(unit, LOG_WARNING, filename, line, 0,
6ff5cc6b
YW
1625 "Invalid multipath route weight, ignoring assignment: %s", p);
1626 return 0;
1627 }
1628 }
1629
1630 r = ordered_set_ensure_allocated(&n->multipath_routes, NULL);
1631 if (r < 0)
1632 return log_oom();
1633
1634 r = ordered_set_put(n->multipath_routes, m);
1635 if (r < 0) {
d96edb2c 1636 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1637 "Failed to store multipath route, ignoring assignment: %m");
1638 return 0;
1639 }
1640
1641 TAKE_PTR(m);
1642 TAKE_PTR(n);
1643 return 0;
1644}
1645
fcbf4cb7
YW
1646int route_section_verify(Route *route, Network *network) {
1647 if (section_is_invalid(route->section))
1648 return -EINVAL;
1649
1650 if (route->family == AF_UNSPEC) {
1651 assert(route->section);
1652
1653 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
1654 "%s: Route section without Gateway=, Destination=, Source=, "
1655 "or PreferredSource= field configured. "
1656 "Ignoring [Route] section from line %u.",
1657 route->section->filename, route->section->line);
1658 }
1659
c0d48bc5
YW
1660 if (!route->table_set && network->vrf) {
1661 route->table = VRF(network->vrf)->table;
1662 route->table_set = true;
1663 }
1664
f5c38922
YW
1665 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
1666 route->table = RT_TABLE_LOCAL;
1667
1668 if (!route->scope_set && route->family != AF_INET6) {
1669 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
1670 route->scope = RT_SCOPE_HOST;
1671 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
1672 route->scope = RT_SCOPE_LINK;
94d6e299
YW
1673 }
1674
fcbf4cb7
YW
1675 if (network->n_static_addresses == 0 &&
1676 in_addr_is_null(route->family, &route->gw) == 0 &&
1677 route->gateway_onlink < 0) {
1678 log_warning("%s: Gateway= without static address configured. "
1679 "Enabling GatewayOnLink= option.",
1680 network->filename);
1681 route->gateway_onlink = true;
1682 }
1683
1684 return 0;
1685}