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