]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
networkd: cleanup FOREACH_WORD
[thirdparty/systemd.git] / src / network / networkd-route.c
CommitLineData
f579559b
TG
1/***
2 This file is part of systemd.
3
4 Copyright 2013 Tom Gundersen <teg@jklm.no>
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
b5efdb8a 20#include "alloc-util.h"
f579559b 21#include "conf-parser.h"
bb7ae737 22#include "in-addr-util.h"
fc2f9534 23#include "netlink-util.h"
6bedfcbb 24#include "networkd-route.h"
fc2f9534 25#include "networkd.h"
6bedfcbb 26#include "parse-util.h"
1c8e710c 27#include "set.h"
07630cea
LP
28#include "string-util.h"
29#include "util.h"
f579559b 30
ed9e361a 31int route_new(Route **ret) {
f0213e37
TG
32 _cleanup_route_free_ Route *route = NULL;
33
34 route = new0(Route, 1);
35 if (!route)
36 return -ENOMEM;
37
38 route->family = AF_UNSPEC;
39 route->scope = RT_SCOPE_UNIVERSE;
ed9e361a 40 route->protocol = RTPROT_UNSPEC;
bb7ae737 41 route->table = RT_TABLE_DEFAULT;
f833694d 42 route->lifetime = USEC_INFINITY;
f0213e37
TG
43
44 *ret = route;
45 route = NULL;
46
47 return 0;
48}
49
f048a16b 50int route_new_static(Network *network, unsigned section, Route **ret) {
f579559b 51 _cleanup_route_free_ Route *route = NULL;
f0213e37 52 int r;
f579559b 53
6ae115c1 54 if (section) {
21b39268 55 route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section));
6ae115c1
TG
56 if (route) {
57 *ret = route;
58 route = NULL;
59
60 return 0;
61 }
62 }
63
ed9e361a 64 r = route_new(&route);
f0213e37
TG
65 if (r < 0)
66 return r;
801bd9e8 67
ed9e361a 68 route->protocol = RTPROT_STATIC;
f579559b 69
6ae115c1 70 if (section) {
cacc1dbf
SS
71 route->section = section;
72
21b39268
LP
73 r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route);
74 if (r < 0)
75 return r;
6ae115c1
TG
76 }
77
21b39268 78 route->network = network;
cacc1dbf 79 LIST_PREPEND(routes, network->static_routes, route);
21b39268 80
f579559b
TG
81 *ret = route;
82 route = NULL;
83
84 return 0;
85}
86
87void route_free(Route *route) {
88 if (!route)
89 return;
90
f048a16b 91 if (route->network) {
3d3d4255 92 LIST_REMOVE(routes, route->network->static_routes, route);
f579559b 93
f048a16b
TG
94 if (route->section)
95 hashmap_remove(route->network->routes_by_section,
16aa63a0 96 UINT_TO_PTR(route->section));
f048a16b 97 }
6ae115c1 98
1c8e710c
TG
99 if (route->link) {
100 set_remove(route->link->routes, route);
101 set_remove(route->link->routes_foreign, route);
102 }
103
f833694d
TG
104 sd_event_source_unref(route->expire);
105
f579559b
TG
106 free(route);
107}
108
bb7ae737
TG
109static void route_hash_func(const void *b, struct siphash *state) {
110 const Route *route = b;
111
112 assert(route);
113
114 siphash24_compress(&route->family, sizeof(route->family), state);
115
116 switch (route->family) {
117 case AF_INET:
118 case AF_INET6:
119 /* Equality of routes are given by the 4-touple
120 (dst_prefix,dst_prefixlen,tos,priority,table) */
2ce40956 121 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
bb7ae737
TG
122 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
123 siphash24_compress(&route->tos, sizeof(route->tos), state);
124 siphash24_compress(&route->priority, sizeof(route->priority), state);
125 siphash24_compress(&route->table, sizeof(route->table), state);
126
127 break;
128 default:
129 /* treat any other address family as AF_UNSPEC */
130 break;
131 }
132}
133
134static int route_compare_func(const void *_a, const void *_b) {
135 const Route *a = _a, *b = _b;
136
137 if (a->family < b->family)
138 return -1;
139 if (a->family > b->family)
140 return 1;
141
142 switch (a->family) {
143 case AF_INET:
144 case AF_INET6:
bb7ae737
TG
145 if (a->dst_prefixlen < b->dst_prefixlen)
146 return -1;
147 if (a->dst_prefixlen > b->dst_prefixlen)
148 return 1;
149
150 if (a->tos < b->tos)
151 return -1;
152 if (a->tos > b->tos)
153 return 1;
154
155 if (a->priority < b->priority)
156 return -1;
157 if (a->priority > b->priority)
158 return 1;
159
160 if (a->table < b->table)
161 return -1;
162 if (a->table > b->table)
163 return 1;
164
2ce40956 165 return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
bb7ae737
TG
166 default:
167 /* treat any other address family as AF_UNSPEC */
168 return 0;
169 }
170}
171
172static const struct hash_ops route_hash_ops = {
173 .hash = route_hash_func,
174 .compare = route_compare_func
175};
176
1c8e710c
TG
177int route_get(Link *link,
178 int family,
179 union in_addr_union *dst,
180 unsigned char dst_prefixlen,
181 unsigned char tos,
182 uint32_t priority,
183 unsigned char table,
184 Route **ret) {
185 Route route = {
186 .family = family,
187 .dst_prefixlen = dst_prefixlen,
188 .tos = tos,
189 .priority = priority,
190 .table = table,
191 }, *existing;
192
193 assert(link);
194 assert(dst);
195 assert(ret);
196
197 route.dst = *dst;
198
199 existing = set_get(link->routes, &route);
200 if (existing) {
201 *ret = existing;
202 return 1;
203 } else {
204 existing = set_get(link->routes_foreign, &route);
205 if (!existing)
206 return -ENOENT;
207 }
208
209 *ret = existing;
210
211 return 0;
212}
213
214static int route_add_internal(Link *link, Set **routes,
215 int family,
216 union in_addr_union *dst,
217 unsigned char dst_prefixlen,
218 unsigned char tos,
219 uint32_t priority,
220 unsigned char table, Route **ret) {
221 _cleanup_route_free_ Route *route = NULL;
222 int r;
223
224 assert(link);
225 assert(routes);
226 assert(dst);
227
228 r = route_new(&route);
229 if (r < 0)
230 return r;
231
232 route->family = family;
233 route->dst = *dst;
234 route->dst_prefixlen = dst_prefixlen;
235 route->tos = tos;
236 route->priority = priority;
237 route->table = table;
238
239 r = set_ensure_allocated(routes, &route_hash_ops);
240 if (r < 0)
241 return r;
242
243 r = set_put(*routes, route);
244 if (r < 0)
245 return r;
246
247 route->link = link;
248
249 if (ret)
250 *ret = route;
251
252 route = NULL;
253
254 return 0;
255}
256
257int route_add_foreign(Link *link,
258 int family,
259 union in_addr_union *dst,
260 unsigned char dst_prefixlen,
261 unsigned char tos,
262 uint32_t priority,
263 unsigned char table, Route **ret) {
264 return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret);
265}
266
267int route_add(Link *link,
268 int family,
269 union in_addr_union *dst,
270 unsigned char dst_prefixlen,
271 unsigned char tos,
272 uint32_t priority,
273 unsigned char table, Route **ret) {
274 Route *route;
275 int r;
276
277 r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route);
278 if (r == -ENOENT) {
279 /* Route does not exist, create a new one */
280 r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route);
281 if (r < 0)
282 return r;
283 } else if (r == 0) {
284 /* Take over a foreign route */
285 r = set_ensure_allocated(&link->routes, &route_hash_ops);
286 if (r < 0)
287 return r;
288
289 r = set_put(link->routes, route);
290 if (r < 0)
291 return r;
292
293 set_remove(link->routes_foreign, route);
294 } else if (r == 1) {
295 /* Route exists, do nothing */
296 ;
297 } else
298 return r;
299
300 *ret = route;
301
302 return 0;
303}
304
305int route_update(Route *route,
306 union in_addr_union *src,
307 unsigned char src_prefixlen,
308 union in_addr_union *gw,
309 union in_addr_union *prefsrc,
310 unsigned char scope,
311 unsigned char protocol) {
312 assert(route);
313 assert(src);
314 assert(gw);
315 assert(prefsrc);
316
317 route->src = *src;
318 route->src_prefixlen = src_prefixlen;
319 route->gw = *gw;
320 route->prefsrc = *prefsrc;
321 route->scope = scope;
322 route->protocol = protocol;
323
324 return 0;
325}
326
327void route_drop(Route *route) {
328 assert(route);
329
330 route_free(route);
331}
332
91b5f997 333int route_remove(Route *route, Link *link,
1c4baffc 334 sd_netlink_message_handler_t callback) {
4afd3348 335 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
336 int r;
337
338 assert(link);
339 assert(link->manager);
340 assert(link->manager->rtnl);
341 assert(link->ifindex > 0);
342 assert(route->family == AF_INET || route->family == AF_INET6);
343
344 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
345 RTM_DELROUTE, route->family,
346 route->protocol);
f647962d
MS
347 if (r < 0)
348 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 349
2ce40956 350 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 351 if (route->family == AF_INET)
2ce40956 352 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 353 else if (route->family == AF_INET6)
2ce40956 354 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
355 if (r < 0)
356 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
357 }
358
359 if (route->dst_prefixlen) {
360 if (route->family == AF_INET)
2ce40956 361 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
5c1d3fc9 362 else if (route->family == AF_INET6)
2ce40956 363 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
364 if (r < 0)
365 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
366
367 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
368 if (r < 0)
369 return log_error_errno(r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
370 }
371
9e7e4408
TG
372 if (route->src_prefixlen) {
373 if (route->family == AF_INET)
2ce40956 374 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 375 else if (route->family == AF_INET6)
2ce40956 376 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408
TG
377 if (r < 0)
378 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
379
380 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
381 if (r < 0)
382 return log_error_errno(r, "Could not set source prefix length: %m");
383 }
384
2ce40956 385 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 386 if (route->family == AF_INET)
2ce40956 387 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 388 else if (route->family == AF_INET6)
2ce40956 389 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
390 if (r < 0)
391 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
392 }
393
5c1d3fc9 394 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
395 if (r < 0)
396 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 397
86655331 398 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
399 if (r < 0)
400 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 401
1c4baffc 402 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
403 if (r < 0)
404 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
5c1d3fc9 405
1c4baffc 406 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
407 if (r < 0)
408 return log_error_errno(r, "Could not send rtnetlink message: %m");
5c1d3fc9 409
563c69c6
TG
410 link_ref(link);
411
5c1d3fc9
UTL
412 return 0;
413}
414
f833694d
TG
415int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
416 Route *route = userdata;
417 int r;
418
419 assert(route);
420
421 r = route_remove(route, route->link, NULL);
422 if (r < 0)
423 log_warning_errno(r, "Could not remove route: %m");
424
425 return 1;
426}
427
f882c247 428int route_configure(Route *route, Link *link,
1c4baffc 429 sd_netlink_message_handler_t callback) {
4afd3348
LP
430 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
431 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
f833694d 432 usec_t lifetime;
f579559b
TG
433 int r;
434
f579559b 435 assert(link);
f882c247
TG
436 assert(link->manager);
437 assert(link->manager->rtnl);
f579559b
TG
438 assert(link->ifindex > 0);
439 assert(route->family == AF_INET || route->family == AF_INET6);
440
151b9b96 441 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
442 RTM_NEWROUTE, route->family,
443 route->protocol);
f647962d
MS
444 if (r < 0)
445 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
f579559b 446
2ce40956 447 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 448 if (route->family == AF_INET)
2ce40956 449 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 450 else if (route->family == AF_INET6)
2ce40956 451 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
452 if (r < 0)
453 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
c953b24c
SS
454
455 r = sd_rtnl_message_route_set_family(req, route->family);
456 if (r < 0)
457 return log_error_errno(r, "Could not set route family: %m");
f579559b
TG
458 }
459
0a0dc69b
TG
460 if (route->dst_prefixlen) {
461 if (route->family == AF_INET)
2ce40956 462 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
0a0dc69b 463 else if (route->family == AF_INET6)
2ce40956 464 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
465 if (r < 0)
466 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
6ae115c1 467
ae4c67a7 468 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
469 if (r < 0)
470 return log_error_errno(r, "Could not set destination prefix length: %m");
1f01fb4f
TG
471 }
472
9e7e4408
TG
473 if (route->src_prefixlen) {
474 if (route->family == AF_INET)
2ce40956 475 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 476 else if (route->family == AF_INET6)
2ce40956 477 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408
TG
478 if (r < 0)
479 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
480
481 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
482 if (r < 0)
483 return log_error_errno(r, "Could not set source prefix length: %m");
484 }
485
2ce40956 486 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 487 if (route->family == AF_INET)
2ce40956 488 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 489 else if (route->family == AF_INET6)
2ce40956 490 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
491 if (r < 0)
492 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
493 }
494
5c1d3fc9 495 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
496 if (r < 0)
497 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 498
3b015d40
TG
499 r = sd_rtnl_message_route_set_flags(req, route->flags);
500 if (r < 0)
c953b24c
SS
501 return log_error_errno(r, "Could not set flags: %m");
502
503 if (route->table != RT_TABLE_DEFAULT) {
504
505 if (route->table < 256) {
506 r = sd_rtnl_message_route_set_table(req, route->table);
507 if (r < 0)
508 return log_error_errno(r, "Could not set route table: %m");
509 } else {
510
511 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
512 if (r < 0)
513 return log_error_errno(r, "Could not set route table: %m");
514
515 /* Table attribute to allow allow more than 256. */
516 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
517 if (r < 0)
518 return log_error_errno(r, "Could not append RTA_TABLE attribute: %m");
519 }
520 }
3b015d40 521
86655331 522 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
523 if (r < 0)
524 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 525
3b015d40
TG
526 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
527 if (r < 0)
528 return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
529
1c4baffc 530 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
531 if (r < 0)
532 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
f579559b 533
1c4baffc 534 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
535 if (r < 0)
536 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 537
563c69c6
TG
538 link_ref(link);
539
f833694d
TG
540 lifetime = route->lifetime;
541
542 r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
1c8e710c
TG
543 if (r < 0)
544 return log_error_errno(r, "Could not add route: %m");
545
f833694d
TG
546 /* TODO: drop expiration handling once it can be pushed into the kernel */
547 route->lifetime = lifetime;
548
549 if (route->lifetime != USEC_INFINITY) {
550 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
551 route->lifetime, 0, route_expire_handler, route);
552 if (r < 0)
553 return log_error_errno(r, "Could not arm expiration timer: %m");
554 }
555
556 sd_event_source_unref(route->expire);
557 route->expire = expire;
558 expire = NULL;
559
f579559b
TG
560 return 0;
561}
562
563int config_parse_gateway(const char *unit,
564 const char *filename,
565 unsigned line,
566 const char *section,
71a61510 567 unsigned section_line,
f579559b
TG
568 const char *lvalue,
569 int ltype,
570 const char *rvalue,
571 void *data,
572 void *userdata) {
44e7b949 573
6ae115c1 574 Network *network = userdata;
f579559b 575 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
576 union in_addr_union buffer;
577 int r, f;
f579559b
TG
578
579 assert(filename);
6ae115c1 580 assert(section);
f579559b
TG
581 assert(lvalue);
582 assert(rvalue);
583 assert(data);
584
92fe133a
TG
585 if (streq(section, "Network")) {
586 /* we are not in an Route section, so treat
587 * this as the special '0' section */
588 section_line = 0;
589 }
590
f048a16b 591 r = route_new_static(network, section_line, &n);
f579559b
TG
592 if (r < 0)
593 return r;
594
44e7b949 595 r = in_addr_from_string_auto(rvalue, &f, &buffer);
f579559b 596 if (r < 0) {
12ca818f 597 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
f579559b
TG
598 return 0;
599 }
600
44e7b949 601 n->family = f;
2ce40956 602 n->gw = buffer;
f579559b
TG
603 n = NULL;
604
605 return 0;
606}
6ae115c1 607
0d07e595
JK
608int config_parse_preferred_src(const char *unit,
609 const char *filename,
610 unsigned line,
611 const char *section,
612 unsigned section_line,
613 const char *lvalue,
614 int ltype,
615 const char *rvalue,
616 void *data,
617 void *userdata) {
618
619 Network *network = userdata;
620 _cleanup_route_free_ Route *n = NULL;
621 union in_addr_union buffer;
622 int r, f;
623
624 assert(filename);
625 assert(section);
626 assert(lvalue);
627 assert(rvalue);
628 assert(data);
629
630 r = route_new_static(network, section_line, &n);
631 if (r < 0)
632 return r;
633
634 r = in_addr_from_string_auto(rvalue, &f, &buffer);
635 if (r < 0) {
636 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
637 "Preferred source is invalid, ignoring assignment: %s", rvalue);
638 return 0;
639 }
640
641 n->family = f;
2ce40956 642 n->prefsrc = buffer;
0d07e595
JK
643 n = NULL;
644
645 return 0;
646}
647
6ae115c1
TG
648int config_parse_destination(const char *unit,
649 const char *filename,
650 unsigned line,
651 const char *section,
652 unsigned section_line,
653 const char *lvalue,
654 int ltype,
655 const char *rvalue,
656 void *data,
657 void *userdata) {
44e7b949 658
6ae115c1
TG
659 Network *network = userdata;
660 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
661 const char *address, *e;
662 union in_addr_union buffer;
9e7e4408 663 unsigned char prefixlen;
44e7b949 664 int r, f;
6ae115c1
TG
665
666 assert(filename);
667 assert(section);
668 assert(lvalue);
669 assert(rvalue);
670 assert(data);
671
f048a16b 672 r = route_new_static(network, section_line, &n);
6ae115c1
TG
673 if (r < 0)
674 return r;
675
9e7e4408 676 /* Destination|Source=address/prefixlen */
6ae115c1 677
ae4c67a7 678 /* address */
6ae115c1 679 e = strchr(rvalue, '/');
44e7b949
LP
680 if (e)
681 address = strndupa(rvalue, e - rvalue);
682 else
683 address = rvalue;
6ae115c1 684
44e7b949 685 r = in_addr_from_string_auto(address, &f, &buffer);
6ae115c1 686 if (r < 0) {
12ca818f 687 log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
6ae115c1
TG
688 return 0;
689 }
690
935c0d26 691 if (f != AF_INET && f != AF_INET6) {
12ca818f 692 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
935c0d26
TG
693 return 0;
694 }
695
ae4c67a7
TG
696 /* prefixlen */
697 if (e) {
9e7e4408 698 r = safe_atou8(e + 1, &prefixlen);
ae4c67a7 699 if (r < 0) {
12ca818f 700 log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
ae4c67a7
TG
701 return 0;
702 }
ae4c67a7 703 } else {
935c0d26 704 switch (f) {
ae4c67a7 705 case AF_INET:
9e7e4408 706 prefixlen = 32;
ae4c67a7
TG
707 break;
708 case AF_INET6:
9e7e4408 709 prefixlen = 128;
ae4c67a7
TG
710 break;
711 }
712 }
713
44e7b949 714 n->family = f;
9e7e4408 715 if (streq(lvalue, "Destination")) {
2ce40956 716 n->dst = buffer;
9e7e4408
TG
717 n->dst_prefixlen = prefixlen;
718 } else if (streq(lvalue, "Source")) {
2ce40956 719 n->src = buffer;
9e7e4408
TG
720 n->src_prefixlen = prefixlen;
721 } else
722 assert_not_reached(lvalue);
723
6ae115c1
TG
724 n = NULL;
725
726 return 0;
727}
5d8e593d
SS
728
729int config_parse_route_priority(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 Network *network = userdata;
740 _cleanup_route_free_ Route *n = NULL;
5d8e593d
SS
741 int r;
742
743 assert(filename);
744 assert(section);
745 assert(lvalue);
746 assert(rvalue);
747 assert(data);
748
749 r = route_new_static(network, section_line, &n);
750 if (r < 0)
751 return r;
752
86655331
TG
753 r = config_parse_uint32(unit, filename, line, section,
754 section_line, lvalue, ltype,
755 rvalue, &n->priority, userdata);
5d8e593d
SS
756 if (r < 0)
757 return r;
758
759 n = NULL;
760
761 return 0;
762}
769b56a3
TG
763
764int config_parse_route_scope(const char *unit,
765 const char *filename,
766 unsigned line,
767 const char *section,
768 unsigned section_line,
769 const char *lvalue,
770 int ltype,
771 const char *rvalue,
772 void *data,
773 void *userdata) {
774 Network *network = userdata;
775 _cleanup_route_free_ Route *n = NULL;
776 int r;
777
778 assert(filename);
779 assert(section);
780 assert(lvalue);
781 assert(rvalue);
782 assert(data);
783
784 r = route_new_static(network, section_line, &n);
785 if (r < 0)
786 return r;
787
788 if (streq(rvalue, "host"))
789 n->scope = RT_SCOPE_HOST;
790 else if (streq(rvalue, "link"))
791 n->scope = RT_SCOPE_LINK;
792 else if (streq(rvalue, "global"))
793 n->scope = RT_SCOPE_UNIVERSE;
794 else {
12ca818f 795 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
796 return 0;
797 }
798
799 n = NULL;
800
801 return 0;
802}
c953b24c
SS
803
804int config_parse_route_table(const char *unit,
805 const char *filename,
806 unsigned line,
807 const char *section,
808 unsigned section_line,
809 const char *lvalue,
810 int ltype,
811 const char *rvalue,
812 void *data,
813 void *userdata) {
814 _cleanup_route_free_ Route *n = NULL;
815 Network *network = userdata;
816 uint32_t k;
817 int r;
818
819 assert(filename);
820 assert(section);
821 assert(lvalue);
822 assert(rvalue);
823 assert(data);
824
825 r = route_new_static(network, section_line, &n);
826 if (r < 0)
827 return r;
828
829 r = safe_atou32(rvalue, &k);
830 if (r < 0) {
831 log_syntax(unit, LOG_ERR, filename, line, r,
832 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
833 return 0;
834 }
835
836 n->table = k;
837
838 n = NULL;
839
840 return 0;
841}