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