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