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