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