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