diff options
Diffstat (limited to 'src/network/networkd-route.c')
-rw-r--r-- | src/network/networkd-route.c | 264 |
1 files changed, 232 insertions, 32 deletions
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 4a74bc69f3..f4bbd06af1 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -21,11 +21,13 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "event-util.h" #include "in-addr-util.h" #include "netlink-util.h" #include "networkd-route.h" #include "networkd.h" #include "parse-util.h" +#include "set.h" #include "string-util.h" #include "util.h" @@ -40,6 +42,7 @@ int route_new(Route **ret) { route->scope = RT_SCOPE_UNIVERSE; route->protocol = RTPROT_UNSPEC; route->table = RT_TABLE_DEFAULT; + route->lifetime = USEC_INFINITY; *ret = route; route = NULL; @@ -95,6 +98,13 @@ void route_free(Route *route) { UINT_TO_PTR(route->section)); } + if (route->link) { + set_remove(route->link->routes, route); + set_remove(route->link->routes_foreign, route); + } + + sd_event_source_unref(route->expire); + free(route); } @@ -110,7 +120,7 @@ static void route_hash_func(const void *b, struct siphash *state) { case AF_INET6: /* Equality of routes are given by the 4-touple (dst_prefix,dst_prefixlen,tos,priority,table) */ - siphash24_compress(&route->dst_addr, FAMILY_ADDRESS_SIZE(route->family), state); + siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state); siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state); siphash24_compress(&route->tos, sizeof(route->tos), state); siphash24_compress(&route->priority, sizeof(route->priority), state); @@ -134,7 +144,6 @@ static int route_compare_func(const void *_a, const void *_b) { switch (a->family) { case AF_INET: case AF_INET6: - //TODO: check IPv6 routes if (a->dst_prefixlen < b->dst_prefixlen) return -1; if (a->dst_prefixlen > b->dst_prefixlen) @@ -155,7 +164,7 @@ static int route_compare_func(const void *_a, const void *_b) { if (a->table > b->table) return 1; - return memcmp(&a->dst_addr, &b->dst_addr, FAMILY_ADDRESS_SIZE(a->family)); + return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family)); default: /* treat any other address family as AF_UNSPEC */ return 0; @@ -167,6 +176,162 @@ static const struct hash_ops route_hash_ops = { .compare = route_compare_func }; +int route_get(Link *link, + int family, + union in_addr_union *dst, + unsigned char dst_prefixlen, + unsigned char tos, + uint32_t priority, + unsigned char table, + Route **ret) { + Route route = { + .family = family, + .dst_prefixlen = dst_prefixlen, + .tos = tos, + .priority = priority, + .table = table, + }, *existing; + + assert(link); + assert(dst); + assert(ret); + + route.dst = *dst; + + existing = set_get(link->routes, &route); + if (existing) { + *ret = existing; + return 1; + } else { + existing = set_get(link->routes_foreign, &route); + if (!existing) + return -ENOENT; + } + + *ret = existing; + + return 0; +} + +static int route_add_internal(Link *link, Set **routes, + int family, + union in_addr_union *dst, + unsigned char dst_prefixlen, + unsigned char tos, + uint32_t priority, + unsigned char table, Route **ret) { + _cleanup_route_free_ Route *route = NULL; + int r; + + assert(link); + assert(routes); + assert(dst); + + r = route_new(&route); + if (r < 0) + return r; + + route->family = family; + route->dst = *dst; + route->dst_prefixlen = dst_prefixlen; + route->tos = tos; + route->priority = priority; + route->table = table; + + r = set_ensure_allocated(routes, &route_hash_ops); + if (r < 0) + return r; + + r = set_put(*routes, route); + if (r < 0) + return r; + + route->link = link; + + if (ret) + *ret = route; + + route = NULL; + + return 0; +} + +int route_add_foreign(Link *link, + int family, + union in_addr_union *dst, + unsigned char dst_prefixlen, + unsigned char tos, + uint32_t priority, + unsigned char table, Route **ret) { + return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret); +} + +int route_add(Link *link, + int family, + union in_addr_union *dst, + unsigned char dst_prefixlen, + unsigned char tos, + uint32_t priority, + unsigned char table, Route **ret) { + Route *route; + int r; + + r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route); + if (r == -ENOENT) { + /* Route does not exist, create a new one */ + r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route); + if (r < 0) + return r; + } else if (r == 0) { + /* Take over a foreign route */ + r = set_ensure_allocated(&link->routes, &route_hash_ops); + if (r < 0) + return r; + + r = set_put(link->routes, route); + if (r < 0) + return r; + + set_remove(link->routes_foreign, route); + } else if (r == 1) { + /* Route exists, do nothing */ + ; + } else + return r; + + *ret = route; + + return 0; +} + +int route_update(Route *route, + union in_addr_union *src, + unsigned char src_prefixlen, + union in_addr_union *gw, + union in_addr_union *prefsrc, + unsigned char scope, + unsigned char protocol) { + assert(route); + assert(src); + assert(gw); + assert(prefsrc); + + route->src = *src; + route->src_prefixlen = src_prefixlen; + route->gw = *gw; + route->prefsrc = *prefsrc; + route->scope = scope; + route->protocol = protocol; + + return 0; +} + +void route_drop(Route *route) { + assert(route); + + route_free(route); +} + int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; @@ -184,20 +349,20 @@ int route_remove(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); - if (!in_addr_is_null(route->family, &route->in_addr)) { + if (!in_addr_is_null(route->family, &route->gw)) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); @@ -208,9 +373,9 @@ int route_remove(Route *route, Link *link, if (route->src_prefixlen) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); @@ -219,11 +384,11 @@ int route_remove(Route *route, Link *link, return log_error_errno(r, "Could not set source prefix length: %m"); } - if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { + if (!in_addr_is_null(route->family, &route->prefsrc)) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } @@ -232,7 +397,7 @@ int route_remove(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not set scope: %m"); - r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics); + r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); @@ -249,9 +414,24 @@ int route_remove(Route *route, Link *link, return 0; } +int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { + Route *route = userdata; + int r; + + assert(route); + + r = route_remove(route, route->link, NULL); + if (r < 0) + log_warning_errno(r, "Could not remove route: %m"); + + return 1; +} + int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; + _cleanup_event_source_unref_ sd_event_source *expire = NULL; + usec_t lifetime; int r; assert(link); @@ -266,20 +446,20 @@ int route_configure(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); - if (!in_addr_is_null(route->family, &route->in_addr)) { + if (!in_addr_is_null(route->family, &route->gw)) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_DST attribute: %m"); @@ -290,9 +470,9 @@ int route_configure(Route *route, Link *link, if (route->src_prefixlen) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); @@ -301,11 +481,11 @@ int route_configure(Route *route, Link *link, return log_error_errno(r, "Could not set source prefix length: %m"); } - if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { + if (!in_addr_is_null(route->family, &route->prefsrc)) { if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); + r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in); else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); + r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } @@ -314,7 +494,7 @@ int route_configure(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not set scope: %m"); - r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->metrics); + r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); @@ -328,6 +508,26 @@ int route_configure(Route *route, Link *link, link_ref(link); + lifetime = route->lifetime; + + r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route); + if (r < 0) + return log_error_errno(r, "Could not add route: %m"); + + /* TODO: drop expiration handling once it can be pushed into the kernel */ + route->lifetime = lifetime; + + if (route->lifetime != USEC_INFINITY) { + r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), + route->lifetime, 0, route_expire_handler, route); + if (r < 0) + return log_error_errno(r, "Could not arm expiration timer: %m"); + } + + sd_event_source_unref(route->expire); + route->expire = expire; + expire = NULL; + return 0; } @@ -370,7 +570,7 @@ int config_parse_gateway(const char *unit, } n->family = f; - n->in_addr = buffer; + n->gw = buffer; n = NULL; return 0; @@ -410,7 +610,7 @@ int config_parse_preferred_src(const char *unit, } n->family = f; - n->prefsrc_addr = buffer; + n->prefsrc = buffer; n = NULL; return 0; @@ -484,10 +684,10 @@ int config_parse_destination(const char *unit, n->family = f; if (streq(lvalue, "Destination")) { - n->dst_addr = buffer; + n->dst = buffer; n->dst_prefixlen = prefixlen; } else if (streq(lvalue, "Source")) { - n->src_addr = buffer; + n->src = buffer; n->src_prefixlen = prefixlen; } else assert_not_reached(lvalue); @@ -521,9 +721,9 @@ int config_parse_route_priority(const char *unit, if (r < 0) return r; - r = config_parse_unsigned(unit, filename, line, section, - section_line, lvalue, ltype, - rvalue, &n->metrics, userdata); + r = config_parse_uint32(unit, filename, line, section, + section_line, lvalue, ltype, + rvalue, &n->priority, userdata); if (r < 0) return r; |