]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: route - track routes
authorTom Gundersen <teg@jklm.no>
Sun, 25 Oct 2015 13:46:21 +0000 (14:46 +0100)
committerTom Gundersen <teg@jklm.no>
Fri, 30 Oct 2015 11:32:48 +0000 (12:32 +0100)
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd.c
src/network/networkd.h
units/systemd-networkd.socket

index 2a69f1c16b665515d1b09cc2f1aa315082f14639..a22041870efbd60d870810e801aafb19e53e7f5c 100644 (file)
@@ -85,6 +85,8 @@ struct Link {
 
         Set *addresses;
         Set *addresses_foreign;
+        Set *routes;
+        Set *routes_foreign;
 
         sd_dhcp_client *dhcp_client;
         sd_dhcp_lease *dhcp_lease;
index 6b2a661ca7f43ffdc385ce7f9b90e79bf6f58791..a5701001c18eec77ddf89769bcffdccc617f65c0 100644 (file)
@@ -279,6 +279,196 @@ static int manager_connect_udev(Manager *m) {
         return 0;
 }
 
+int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+        Manager *m = userdata;
+        Link *link = NULL;
+        uint16_t type;
+        uint32_t ifindex, priority = 0;
+        unsigned char protocol, scope, tos, table;
+        int family;
+        unsigned char dst_prefixlen, src_prefixlen;
+        union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {};
+        Route *route = NULL;
+        int r;
+
+        assert(rtnl);
+        assert(message);
+        assert(m);
+
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_warning_errno(r, "rtnl: failed to receive route: %m");
+
+                return 0;
+        }
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type: %m");
+                return 0;
+        } else if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
+                log_warning("rtnl: received unexpected message type when processing route");
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
+        if (r == -ENODATA) {
+                log_debug("rtnl: received route without ifindex, ignoring");
+                return 0;
+        } else if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get ifindex from route, ignoring: %m");
+                return 0;
+        } else if (ifindex <= 0) {
+                log_warning("rtnl: received route message with invalid ifindex, ignoring: %d", ifindex);
+                return 0;
+        } else {
+                r = link_get(m, ifindex, &link);
+                if (r < 0 || !link) {
+                        /* when enumerating we might be out of sync, but we will
+                         * get the route again, so just ignore it */
+                        if (!m->enumerating)
+                                log_warning("rtnl: received route for nonexistent link (%d), ignoring", ifindex);
+                        return 0;
+                }
+        }
+
+        r = sd_rtnl_message_route_get_family(message, &family);
+        if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
+                log_link_warning(link, "rtnl: received address with invalid family, ignoring.");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_protocol(message, &protocol);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get route protocol: %m");
+                return 0;
+        }
+
+        switch (family) {
+        case AF_INET:
+                r = sd_netlink_message_read_in_addr(message, RTA_DST, &dst.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &gw.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in_addr(message, RTA_SRC, &src.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &prefsrc.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+                        return 0;
+                }
+
+                break;
+
+        case AF_INET6:
+                r = sd_netlink_message_read_in6_addr(message, RTA_DST, &dst.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &gw.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &src.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+                        return 0;
+                }
+
+                r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &prefsrc.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+                        return 0;
+                }
+
+                break;
+
+        default:
+                log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_dst_prefixlen(message, &dst_prefixlen);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route with invalid destination prefixlen, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_src_prefixlen(message, &src_prefixlen);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route with invalid source prefixlen, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_scope(message, &scope);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route with invalid scope, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_tos(message, &tos);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route with invalid tos, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_rtnl_message_route_get_table(message, &table);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &priority);
+        if (r < 0 && r != -ENODATA) {
+                log_link_warning_errno(link, r, "rtnl: received route with invalid priority, ignoring: %m");
+                return 0;
+        }
+
+        route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+
+        switch (type) {
+        case RTM_NEWROUTE:
+                if (!route) {
+                        /* A route appeared that we did not request */
+                        r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
+                        if (r < 0)
+                                return 0;
+                }
+
+                route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol);
+
+                break;
+
+        case RTM_DELROUTE:
+
+                if (route)
+                        route_drop(route);
+
+                break;
+        default:
+                assert_not_reached("Received invalid RTNL message type");
+        }
+
+        return 1;
+}
+
 int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
         Manager *m = userdata;
         Link *link = NULL;
@@ -377,7 +567,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         default:
-                assert_not_reached("invalid address family");
+                log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
         }
 
         if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) {
@@ -572,6 +762,14 @@ static int manager_connect_rtnl(Manager *m) {
         if (r < 0)
                 return r;
 
+        r = sd_netlink_add_match(m->rtnl, RTM_NEWROUTE, &manager_rtnl_process_route, m);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_add_match(m->rtnl, RTM_DELROUTE, &manager_rtnl_process_route, m);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -1019,6 +1217,41 @@ int manager_rtnl_enumerate_addresses(Manager *m) {
         return r;
 }
 
+int manager_rtnl_enumerate_routes(Manager *m) {
+        _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+        sd_netlink_message *route;
+        int r;
+
+        assert(m);
+        assert(m->rtnl);
+
+        r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_request_dump(req, true);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(m->rtnl, req, 0, &reply);
+        if (r < 0)
+                return r;
+
+        for (route = reply; route; route = sd_netlink_message_next(route)) {
+                int k;
+
+                m->enumerating = true;
+
+                k = manager_rtnl_process_route(m->rtnl, route, m);
+                if (k < 0)
+                        r = k;
+
+                m->enumerating = false;
+        }
+
+        return r;
+}
+
 int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
         AddressPool *p;
         int r;
index 078d9c16184f4e25e1ba722c1198f25ff7a8ebaf..7c0d03cdc73aff49b25ad869320635a9732f635a 100644 (file)
@@ -26,6 +26,7 @@
 #include "networkd-route.h"
 #include "networkd.h"
 #include "parse-util.h"
+#include "set.h"
 #include "string-util.h"
 #include "util.h"
 
@@ -95,6 +96,11 @@ 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);
+        }
+
         free(route);
 }
 
@@ -166,6 +172,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;
@@ -327,6 +489,10 @@ int route_configure(Route *route, Link *link,
 
         link_ref(link);
 
+        r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not add route: %m");
+
         return 0;
 }
 
index b1ed5d108542084dbb856613abe9df92476cd36f..785bd1663a00a3423648a6a1a86011f1ef4becfe 100644 (file)
@@ -30,6 +30,8 @@ struct Route {
         Network *network;
         unsigned section;
 
+        Link *link;
+
         int family;
         unsigned char dst_prefixlen;
         unsigned char src_prefixlen;
@@ -53,6 +55,12 @@ void route_free(Route *route);
 int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback);
 int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback);
 
+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);
+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);
+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);
+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);
+void route_drop(Route *route);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free);
 #define _cleanup_route_free_ _cleanup_(route_freep)
 
index c03ac69e277de0ce32cd39cc479813d1da722331..ef394e0c04570c7518b51aaa884e67e9ec3fdf05 100644 (file)
@@ -109,6 +109,12 @@ int main(int argc, char *argv[]) {
                 goto out;
         }
 
+        r = manager_rtnl_enumerate_routes(m);
+        if (r < 0) {
+                log_error_errno(r, "Could not enumerate routes: %m");
+                goto out;
+        }
+
         log_info("Enumeration completed");
 
         sd_notify(false,
index 6c5a9939beca50b987ba7e4455723b7a1076bf98..97665fac7aa4e2fc4e108268fef09e44d5d6e85c 100644 (file)
@@ -82,8 +82,10 @@ bool manager_should_reload(Manager *m);
 
 int manager_rtnl_enumerate_links(Manager *m);
 int manager_rtnl_enumerate_addresses(Manager *m);
+int manager_rtnl_enumerate_routes(Manager *m);
 
 int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
+int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 
 int manager_send_changed(Manager *m, const char *property, ...) _sentinel_;
 void manager_dirty(Manager *m);
index 2c20935d831e8b064a16be0d7499a84134fed9f7..9e4e9dd338a36254eea76584f64c75bc583a2e3c 100644 (file)
@@ -14,7 +14,7 @@ Before=sockets.target
 
 [Socket]
 ReceiveBuffer=8M
-ListenNetlink=route 273
+ListenNetlink=route 1361
 PassCredentials=yes
 
 [Install]