]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: also manage routes without RTA_OIF attribute
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 7 Oct 2020 00:41:52 +0000 (09:41 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 7 Oct 2020 13:22:10 +0000 (15:22 +0200)
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-route.c
src/network/networkd-route.h

index ed4d7c34e4a66f90b9197a86b31f4356cdf07b4c..b31b68756d45159f81c1850f3904e8141cb741db 100644 (file)
@@ -38,7 +38,7 @@ static int dhcp4_release_old_lease(Link *link) {
         link_dirty(link);
 
         SET_FOREACH(route, link->dhcp_routes_old) {
-                k = route_remove(route, link, NULL);
+                k = route_remove(route, NULL, link, NULL);
                 if (k < 0)
                         r = k;
         }
@@ -512,7 +512,7 @@ static int dhcp4_remove_all(Link *link) {
         assert(link);
 
         SET_FOREACH(route, link->dhcp_routes) {
-                k = route_remove(route, link, dhcp4_remove_route_handler);
+                k = route_remove(route, NULL, link, dhcp4_remove_route_handler);
                 if (k < 0)
                         r = k;
                 else
index ea331c95db45d34b9d58d258ba63d6a05da30f63..5217497f9821878af5e055be3629918faace4dde 100644 (file)
@@ -164,7 +164,7 @@ static int dhcp6_pd_remove_old(Link *link, bool force) {
         link_dirty(link);
 
         SET_FOREACH(route, link->dhcp6_pd_routes_old) {
-                k = route_remove(route, link, NULL);
+                k = route_remove(route, NULL, link, NULL);
                 if (k < 0)
                         r = k;
 
@@ -208,7 +208,7 @@ int dhcp6_pd_remove(Link *link) {
         link_dirty(link);
 
         SET_FOREACH(route, link->dhcp6_pd_routes) {
-                k = route_remove(route, link, NULL);
+                k = route_remove(route, NULL, link, NULL);
                 if (k < 0)
                         r = k;
 
@@ -704,7 +704,7 @@ static int dhcp6_remove_old(Link *link, bool force) {
         link_dirty(link);
 
         SET_FOREACH(route, link->dhcp6_routes_old) {
-                k = route_remove(route, link, NULL);
+                k = route_remove(route, NULL, link, NULL);
                 if (k < 0)
                         r = k;
         }
@@ -740,7 +740,7 @@ static int dhcp6_remove(Link *link) {
         link_dirty(link);
 
         SET_FOREACH(route, link->dhcp6_routes) {
-                k = route_remove(route, link, NULL);
+                k = route_remove(route, NULL, link, NULL);
                 if (k < 0)
                         r = k;
         }
index e8d270987d9667af896526d1e54b9abf1d983181..2cd68300727dcc4aeb9b34f6c9470e661b7128cc 100644 (file)
@@ -886,6 +886,9 @@ void manager_free(Manager *m) {
         m->rules_foreign = set_free(m->rules_foreign);
         set_free(m->rules_saved);
 
+        m->routes = set_free(m->routes);
+        m->routes_foreign = set_free(m->routes_foreign);
+
         sd_netlink_unref(m->rtnl);
         sd_netlink_unref(m->genl);
         sd_resolve_unref(m->resolve);
index 142a6eb3582a7185cfd7700eec151b8169df47fe..ac7de58c649a8ee34dc27d932426903aceaa1256 100644 (file)
@@ -61,6 +61,10 @@ struct Manager {
         Set *rules_foreign;
         Set *rules_saved;
 
+        /* Manager stores routes without RTA_OIF attribute. */
+        Set *routes;
+        Set *routes_foreign;
+
         /* For link speed meter*/
         bool use_speed_meter;
         sd_event_source *speed_meter_event_source;
index 2599ec22324628263c13d3daebb65bb0899ff58c..e2e049201a346b094081da6eb5812ff6576b32ca 100644 (file)
@@ -163,7 +163,7 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
 
         SET_FOREACH(nr, link->ndisc_routes)
                 if (nr->marked && IN6_ARE_ADDR_EQUAL(&nr->router, router)) {
-                        k = route_remove(nr->route, link, NULL);
+                        k = route_remove(nr->route, NULL, link, NULL);
                         if (k < 0)
                                 r = k;
                 }
index 870de18b8cfb0077faf5ca3ad8449f7d00224549..c3de2c7c084f2df4c7156fc8b4b4d43863c10a63 100644 (file)
@@ -272,6 +272,11 @@ Route *route_free(Route *route) {
                                 free(set_remove(route->link->ndisc_routes, n));
         }
 
+        if (route->manager) {
+                set_remove(route->manager->routes, route);
+                set_remove(route->manager->routes_foreign, route);
+        }
+
         ordered_set_free_free(route->multipath_routes);
 
         sd_event_source_unref(route->expire);
@@ -404,36 +409,51 @@ static bool route_equal(Route *r1, Route *r2) {
         return route_compare_func(r1, r2) == 0;
 }
 
-static int route_get(Link *link, Route *in, Route **ret) {
-
+static int route_get(Manager *manager, Link *link, Route *in, Route **ret) {
         Route *existing;
 
-        assert(link);
+        assert(manager || link);
         assert(in);
 
-        existing = set_get(link->routes, in);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 1;
-        }
+        if (link) {
+                existing = set_get(link->routes, in);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 1;
+                }
 
-        existing = set_get(link->routes_foreign, in);
-        if (existing) {
-                if (ret)
-                        *ret = existing;
-                return 0;
+                existing = set_get(link->routes_foreign, in);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 0;
+                }
+        } else {
+                existing = set_get(manager->routes, in);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 1;
+                }
+
+                existing = set_get(manager->routes_foreign, in);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 0;
+                }
         }
 
         return -ENOENT;
 }
 
-static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) {
+static int route_add_internal(Manager *manager, Link *link, Set **routes, Route *in, Route **ret) {
 
         _cleanup_(route_freep) Route *route = NULL;
         int r;
 
-        assert(link);
+        assert(manager || link);
         assert(routes);
         assert(in);
 
@@ -465,6 +485,7 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret)
                 return -EEXIST;
 
         route->link = link;
+        route->manager = manager;
 
         if (ret)
                 *ret = route;
@@ -474,28 +495,39 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret)
         return 0;
 }
 
-static int route_add_foreign(Link *link, Route *in, Route **ret) {
-        return route_add_internal(link, &link->routes_foreign, in, ret);
+static int route_add_foreign(Manager *manager, Link *link, Route *in, Route **ret) {
+        assert(manager || link);
+        return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
 }
 
-static int route_add(Link *link, Route *in, Route **ret) {
-
+static int route_add(Manager *manager, Link *link, Route *in, Route **ret) {
         Route *route;
         int r;
 
-        r = route_get(link, in, &route);
+        assert(manager || link);
+        assert(in);
+
+        r = route_get(manager, link, in, &route);
         if (r == -ENOENT) {
                 /* Route does not exist, create a new one */
-                r = route_add_internal(link, &link->routes, in, &route);
+                r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route);
                 if (r < 0)
                         return r;
         } else if (r == 0) {
                 /* Take over a foreign route */
-                r = set_ensure_put(&link->routes, &route_hash_ops, route);
-                if (r < 0)
-                        return r;
+                if (link) {
+                        r = set_ensure_put(&link->routes, &route_hash_ops, route);
+                        if (r < 0)
+                                return r;
 
-                set_remove(link->routes_foreign, route);
+                        set_remove(link->routes_foreign, route);
+                } else {
+                        r = set_ensure_put(&manager->routes, &route_hash_ops, route);
+                        if (r < 0)
+                                return r;
+
+                        set_remove(manager->routes_foreign, route);
+                }
         } else if (r == 1) {
                 /* Route exists, do nothing */
                 ;
@@ -512,10 +544,9 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
         int r;
 
         assert(m);
-        assert(link);
-        assert(link->ifname);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+        /* Note that link may be NULL. */
+        if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
@@ -525,19 +556,22 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
         return 1;
 }
 
-int route_remove(Route *route, Link *link,
-                 link_netlink_message_handler_t callback) {
+int route_remove(
+                Route *route,
+                Manager *manager,
+                Link *link,
+                link_netlink_message_handler_t callback) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
-        assert(link);
-        assert(link->manager);
-        assert(link->manager->rtnl);
-        assert(link->ifindex > 0);
+        assert(link || manager);
         assert(IN_SET(route->family, AF_INET, AF_INET6));
 
-        r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
+        if (!manager)
+                manager = link->manager;
+
+        r = sd_rtnl_message_new_route(manager->rtnl, &req,
                                       RTM_DELROUTE, route->family,
                                       route->protocol);
         if (r < 0)
@@ -618,7 +652,8 @@ int route_remove(Route *route, Link *link,
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
 
-        link_ref(link);
+        if (link)
+                link_ref(link);
 
         return 0;
 }
@@ -668,9 +703,9 @@ int link_drop_foreign_routes(Link *link) {
                         continue;
 
                 if (link_is_static_route_configured(link, route))
-                        k = route_add(link, route, NULL);
+                        k = route_add(NULL, link, route, NULL);
                 else
-                        k = route_remove(route, link, NULL);
+                        k = route_remove(route, NULL, link, NULL);
                 if (k < 0 && r >= 0)
                         r = k;
         }
@@ -689,7 +724,7 @@ int link_drop_routes(Link *link) {
                 if (route->protocol == RTPROT_KERNEL)
                         continue;
 
-                k = route_remove(route, link, NULL);
+                k = route_remove(route, NULL, link, NULL);
                 if (k < 0 && r >= 0)
                         r = k;
         }
@@ -703,7 +738,7 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
 
         assert(route);
 
-        r = route_remove(route, route->link, NULL);
+        r = route_remove(route, route->manager, route->link, NULL);
         if (r < 0)
                 log_link_warning_errno(route->link, r, "Could not remove route: %m");
         else
@@ -810,7 +845,7 @@ int route_configure(
         assert(IN_SET(route->family, AF_INET, AF_INET6));
         assert(callback);
 
-        if (route_get(link, route, NULL) <= 0 &&
+        if (route_get(link->manager, link, route, NULL) <= 0 &&
             set_size(link->routes) >= routes_max())
                 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
                                             "Too many routes are configured, refusing: %m");
@@ -988,7 +1023,10 @@ int route_configure(
 
         link_ref(link);
 
-        r = route_add(link, route, &route);
+        if (IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW) || !ordered_set_isempty(route->multipath_routes))
+                r = route_add(link->manager, NULL, route, &route);
+        else
+                r = route_add(NULL, link, route, &route);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not add route: %m");
 
@@ -1123,24 +1161,23 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
         }
 
         r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
-        if (r == -ENODATA) {
-                log_debug("rtnl: received route message without ifindex, ignoring");
-                return 0;
-        } else if (r < 0) {
+        if (r < 0 && r != -ENODATA) {
                 log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
                 return 0;
-        } else if (ifindex <= 0) {
-                log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
-                return 0;
-        }
+        } else if (r >= 0) {
+                if (ifindex <= 0) {
+                        log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
+                        return 0;
+                }
 
-        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 message for link (%d) we do not know about, ignoring", ifindex);
-                return 0;
+                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 message for link (%d) we do not know about, ignoring", ifindex);
+                        return 0;
+                }
         }
 
         r = route_new(&tmp);
@@ -1290,7 +1327,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
                 }
         }
 
-        (void) route_get(link, tmp, &route);
+        (void) route_get(m, link, tmp, &route);
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
@@ -1311,7 +1348,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
 
                 log_link_debug(link,
                                "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
-                               (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" :
+                               (!route && !m->manage_foreign_routes) ? "Ignoring received foreign" :
                                type == RTM_DELROUTE ? "Forgetting" :
                                route ? "Received remembered" : "Remembering",
                                strna(buf_dst), strempty(buf_dst_prefixlen),
@@ -1324,9 +1361,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
 
         switch (type) {
         case RTM_NEWROUTE:
-                if (!route && link->manager->manage_foreign_routes) {
+                if (!route && m->manage_foreign_routes) {
                         /* A route appeared that we did not request */
-                        r = route_add_foreign(link, tmp, &route);
+                        r = route_add_foreign(m, link, tmp, &route);
                         if (r < 0) {
                                 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
                                 return 0;
@@ -1420,7 +1457,7 @@ int link_deserialize_routes(Link *link, const char *routes) {
                         continue;
                 }
 
-                r = route_add(link, tmp, &route);
+                r = route_add(NULL, link, tmp, &route);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Failed to add route: %m");
 
index 3347c7c57b4a00f12d092d7038cff2dd0b38d069..5a82c3ac58e3aad572c31104fad5ec1bf867614b 100644 (file)
@@ -31,6 +31,7 @@ typedef struct Route {
         NetworkConfigSection *section;
 
         Link *link;
+        Manager *manager;
 
         int family;
         int quickack;
@@ -74,7 +75,7 @@ Route *route_free(Route *route);
 DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
 
 int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
-int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
+int route_remove(Route *route, Manager *manager, Link *link, link_netlink_message_handler_t callback);
 
 int link_set_routes(Link *link);
 int link_drop_routes(Link *link);