chiark / gitweb /
networkd: support route scopes
[elogind.git] / src / network / networkd-route.c
index 00fd9528c48586dee2cef4daa9e2efa3a208fe43..c2d1ffca25444383ac1a42421406746673792de2 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/if.h>
 
 #include "networkd.h"
+#include "networkd-link.h"
 
 #include "utf8.h"
 #include "util.h"
@@ -32,9 +33,8 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
         _cleanup_route_free_ Route *route = NULL;
 
         if (section) {
-                uint64_t key = section;
-
-                route = hashmap_get(network->routes_by_section, &key);
+                route = hashmap_get(network->routes_by_section,
+                                    UINT_TO_PTR(section));
                 if (route) {
                         *ret = route;
                         route = NULL;
@@ -57,7 +57,8 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
 
         if (section) {
                 route->section = section;
-                hashmap_put(network->routes_by_section, &route->section, route);
+                hashmap_put(network->routes_by_section,
+                            UINT_TO_PTR(route->section), route);
         }
 
         *ret = route;
@@ -92,7 +93,7 @@ void route_free(Route *route) {
 
                 if (route->section)
                         hashmap_remove(route->network->routes_by_section,
-                                       &route->section);
+                                       UINT_TO_PTR(route->section));
         }
 
         free(route);
@@ -112,18 +113,16 @@ int route_drop(Route *route, Link *link,
         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
                                       RTM_DELROUTE, route->family,
                                       route->protocol);
-        if (r < 0) {
-                log_error("Could not create RTM_DELROUTE message: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
 
-        if (route->family == AF_INET)
-                r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
-        else if (route->family == AF_INET6)
-                r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
-        if (r < 0) {
-                log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
-                return r;
+        if (!in_addr_is_null(route->family, &route->in_addr)) {
+                if (route->family == AF_INET)
+                        r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
+                else if (route->family == AF_INET6)
+                        r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
         }
 
         if (route->dst_prefixlen) {
@@ -131,41 +130,51 @@ int route_drop(Route *route, Link *link,
                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
                 else if (route->family == AF_INET6)
                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
-                if (r < 0) {
-                        log_error("Could not append RTA_DST attribute: %s", strerror(-r));
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_DST attribute: %m");
 
                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
-                if (r < 0) {
-                        log_error("Could not set destination prefix length: %s", strerror(-r));
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Could not set destination prefix length: %m");
         }
 
-        r = sd_rtnl_message_route_set_scope(req, route->scope);
-        if (r < 0) {
-                log_error("Could not set scope: %s", strerror(-r));
-                return r;
+        if (route->src_prefixlen) {
+                if (route->family == AF_INET)
+                        r = sd_rtnl_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+                else if (route->family == AF_INET6)
+                        r = sd_rtnl_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_DST attribute: %m");
+
+                r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set source prefix length: %m");
         }
 
-        r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
-        if (r < 0) {
-                log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r));
-                return r;
+        if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
+                if (route->family == AF_INET)
+                        r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
+                else if (route->family == AF_INET6)
+                        r = sd_rtnl_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
         }
 
+        r = sd_rtnl_message_route_set_scope(req, route->scope);
+        if (r < 0)
+                return log_error_errno(r, "Could not set scope: %m");
+
+        r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
+        if (r < 0)
+                return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
+
         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
-        if (r < 0) {
-                log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
 
         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
-        if (r < 0) {
-                log_error("Could not send rtnetlink message: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not send rtnetlink message: %m");
 
         link_ref(link);
 
@@ -186,18 +195,16 @@ int route_configure(Route *route, Link *link,
         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
                                       RTM_NEWROUTE, route->family,
                                       route->protocol);
-        if (r < 0) {
-                log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
 
-        if (route->family == AF_INET)
-                r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
-        else if (route->family == AF_INET6)
-                r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
-        if (r < 0) {
-                log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
-                return r;
+        if (!in_addr_is_null(route->family, &route->in_addr)) {
+                if (route->family == AF_INET)
+                        r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
+                else if (route->family == AF_INET6)
+                        r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
         }
 
         if (route->dst_prefixlen) {
@@ -205,41 +212,51 @@ int route_configure(Route *route, Link *link,
                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
                 else if (route->family == AF_INET6)
                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
-                if (r < 0) {
-                        log_error("Could not append RTA_DST attribute: %s", strerror(-r));
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_DST attribute: %m");
 
                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
-                if (r < 0) {
-                        log_error("Could not set destination prefix length: %s", strerror(-r));
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Could not set destination prefix length: %m");
         }
 
-        r = sd_rtnl_message_route_set_scope(req, route->scope);
-        if (r < 0) {
-                log_error("Could not set scope: %s", strerror(-r));
-                return r;
+        if (route->src_prefixlen) {
+                if (route->family == AF_INET)
+                        r = sd_rtnl_message_append_in_addr(req, RTA_SRC, &route->src_addr.in);
+                else if (route->family == AF_INET6)
+                        r = sd_rtnl_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
+
+                r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
+                if (r < 0)
+                        return log_error_errno(r, "Could not set source prefix length: %m");
         }
 
-        r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
-        if (r < 0) {
-                log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r));
-                return r;
+        if (!in_addr_is_null(route->family, &route->prefsrc_addr)) {
+                if (route->family == AF_INET)
+                        r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in);
+                else if (route->family == AF_INET6)
+                        r = sd_rtnl_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6);
+                if (r < 0)
+                        return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
         }
 
+        r = sd_rtnl_message_route_set_scope(req, route->scope);
+        if (r < 0)
+                return log_error_errno(r, "Could not set scope: %m");
+
+        r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics);
+        if (r < 0)
+                return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
+
         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
-        if (r < 0) {
-                log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
 
         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
-        if (r < 0) {
-                log_error("Could not send rtnetlink message: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not send rtnetlink message: %m");
 
         link_ref(link);
 
@@ -256,10 +273,11 @@ int config_parse_gateway(const char *unit,
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+
         Network *network = userdata;
         _cleanup_route_free_ Route *n = NULL;
-        _cleanup_free_ char *route = NULL;
-        int r;
+        union in_addr_union buffer;
+        int r, f;
 
         assert(filename);
         assert(section);
@@ -277,13 +295,15 @@ int config_parse_gateway(const char *unit,
         if (r < 0)
                 return r;
 
-        r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
+        r = in_addr_from_string_auto(rvalue, &f, &buffer);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                           "Route is invalid, ignoring assignment: %s", route);
+                           "Route is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
+        n->family = f;
+        n->in_addr = buffer;
         n = NULL;
 
         return 0;
@@ -299,11 +319,13 @@ int config_parse_destination(const char *unit,
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+
         Network *network = userdata;
         _cleanup_route_free_ Route *n = NULL;
-        _cleanup_free_ char *address = NULL;
-        const char *e;
-        int r;
+        const char *address, *e;
+        union in_addr_union buffer;
+        unsigned char prefixlen;
+        int r, f;
 
         assert(filename);
         assert(section);
@@ -315,51 +337,57 @@ int config_parse_destination(const char *unit,
         if (r < 0)
                 return r;
 
-        /* Destination=address/prefixlen */
+        /* Destination|Source=address/prefixlen */
 
         /* address */
         e = strchr(rvalue, '/');
-        if (e) {
-                address = strndup(rvalue, e - rvalue);
-                if (!address)
-                        return log_oom();
-        } else {
-                address = strdup(rvalue);
-                if (!address)
-                        return log_oom();
-        }
+        if (e)
+                address = strndupa(rvalue, e - rvalue);
+        else
+                address = rvalue;
 
-        r = net_parse_inaddr(address, &n->family, &n->dst_addr);
+        r = in_addr_from_string_auto(address, &f, &buffer);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
                            "Destination is invalid, ignoring assignment: %s", address);
                 return 0;
         }
 
+        if (f != AF_INET && f != AF_INET6) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Unknown address family, ignoring assignment: %s", address);
+                return 0;
+        }
+
         /* prefixlen */
         if (e) {
-                unsigned i;
-
-                r = safe_atou(e + 1, &i);
+                r = safe_atou8(e + 1, &prefixlen);
                 if (r < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Route destination prefix length is invalid, "
-                                   "ignoring assignment: %s", e + 1);
+                                   "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
                         return 0;
                 }
-
-                n->dst_prefixlen = (unsigned char) i;
         } else {
-                switch (n->family) {
+                switch (f) {
                         case AF_INET:
-                                n->dst_prefixlen = 32;
+                                prefixlen = 32;
                                 break;
                         case AF_INET6:
-                                n->dst_prefixlen = 128;
+                                prefixlen = 128;
                                 break;
                 }
         }
 
+        n->family = f;
+        if (streq(lvalue, "Destination")) {
+                n->dst_addr = buffer;
+                n->dst_prefixlen = prefixlen;
+        } else if (streq(lvalue, "Source")) {
+                n->src_addr = buffer;
+                n->src_prefixlen = prefixlen;
+        } else
+                assert_not_reached(lvalue);
+
         n = NULL;
 
         return 0;
@@ -399,3 +427,44 @@ int config_parse_route_priority(const char *unit,
 
         return 0;
 }
+
+int config_parse_route_scope(const char *unit,
+                             const char *filename,
+                             unsigned line,
+                             const char *section,
+                             unsigned section_line,
+                             const char *lvalue,
+                             int ltype,
+                             const char *rvalue,
+                             void *data,
+                             void *userdata) {
+        Network *network = userdata;
+        _cleanup_route_free_ Route *n = NULL;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = route_new_static(network, section_line, &n);
+        if (r < 0)
+                return r;
+
+        if (streq(rvalue, "host"))
+                n->scope = RT_SCOPE_HOST;
+        else if (streq(rvalue, "link"))
+                n->scope = RT_SCOPE_LINK;
+        else if (streq(rvalue, "global"))
+                n->scope = RT_SCOPE_UNIVERSE;
+        else {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Unknown route scope: %s", rvalue);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}