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