]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: add fallback logic for old kernels 12818/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 22 Jul 2019 02:25:31 +0000 (11:25 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 22 Jul 2019 02:36:11 +0000 (11:36 +0900)
It seems that old kernels do not support prefix routes with
non-default route tables. This adds a fallback logic when adding route
fails. In that case, prefix route is created by kernel and the default
route table is used.

src/network/networkd-dhcp4.c
src/network/networkd-link.h
src/network/networkd-manager.h
test/test-network/systemd-networkd-tests.py

index 356b9e9d316a331bfeff242583338713cb5b939b..19743d203778e0c57ed4eac7ea3cfed44759441f 100644 (file)
@@ -18,7 +18,9 @@
 static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
 static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
 static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address);
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, link_netlink_message_handler_t callback);
+static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link);
+static int dhcp_lease_renew(sd_dhcp_client *client, Link *link);
 
 void dhcp4_release_old_lease(Link *link) {
         struct in_addr address = {}, address_old = {};
@@ -38,7 +40,7 @@ void dhcp4_release_old_lease(Link *link) {
         (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false);
 
         if (!in4_addr_equal(&address_old, &address))
-                (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old);
+                (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL);
 
         link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
         link_dirty(link);
@@ -56,13 +58,35 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
+        if (r == -ENETUNREACH && !link->dhcp4_route_retrying) {
+
+                /* It seems kernel does not support that the prefix route cannot be configured with
+                 * route table. Let's once drop the config and reconfigure them later. */
+
+                log_link_debug_errno(link, r, "Could not set DHCPv4 route, retrying later: %m");
+                link->dhcp4_route_failed = true;
+                link->manager->dhcp4_prefix_root_cannot_set_table = true;
+        } else if (r < 0 && r != -EEXIST) {
                 log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
                 link_enter_failed(link);
                 return 1;
         }
 
         if (link->dhcp4_messages == 0) {
+                if (link->dhcp4_route_failed) {
+                        struct in_addr address = {};
+
+                        link->dhcp4_route_failed = false;
+                        link->dhcp4_route_retrying = true;
+
+                        (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+                        (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
+                        (void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
+                        (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
+                        (void) dhcp_remove_address(link, link->dhcp_lease, &address, dhcp_remove_address_handler);
+
+                        return 1;
+                }
                 link->dhcp4_configured = true;
                 /* New address and routes are configured now. Let's release old lease. */
                 dhcp4_release_old_lease(link);
@@ -87,7 +111,8 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se
 
 static bool link_noprefixroute(Link *link) {
         return link->network->dhcp_route_table_set &&
-                link->network->dhcp_route_table != RT_TABLE_MAIN;
+                link->network->dhcp_route_table != RT_TABLE_MAIN &&
+                !link->manager->dhcp4_prefix_root_cannot_set_table;
 }
 
 static int dhcp_route_configure(Route **route, Link *link) {
@@ -500,7 +525,32 @@ static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct
         return 0;
 }
 
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address) {
+static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+
+        /* This is only used when retrying to assign the address received from DHCPv4 server.
+         * See dhcp4_route_handler(). */
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0)
+                log_link_debug_errno(link, r, "Failed to remove DHCPv4 address, ignoring: %m");
+        else
+                (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+        (void) dhcp_lease_renew(link->dhcp_client, link);
+        return 1;
+}
+
+static int dhcp_remove_address(
+                        Link *link, sd_dhcp_lease *lease,
+                        const struct in_addr *address,
+                        link_netlink_message_handler_t callback) {
+
         _cleanup_(address_freep) Address *a = NULL;
         struct in_addr netmask;
         int r;
@@ -521,7 +571,7 @@ static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in
         if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
                 a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
 
-        (void) address_remove(a, link, NULL);
+        (void) address_remove(a, link, callback);
 
         return 0;
 }
@@ -590,7 +640,7 @@ static int dhcp_lease_lost(Link *link) {
         (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
         (void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
         (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
-        (void) dhcp_remove_address(link, link->dhcp_lease, &address);
+        (void) dhcp_remove_address(link, link->dhcp_lease, &address, NULL);
         (void) dhcp_reset_mtu(link);
         (void) dhcp_reset_hostname(link);
 
index 91e2bf06b50c55bd757d703ae9487eebf892b7b0..429a053fdf038da172c117312933854fb533c0db 100644 (file)
@@ -87,6 +87,8 @@ typedef struct Link {
         char *lease_file;
         uint32_t original_mtu;
         unsigned dhcp4_messages;
+        bool dhcp4_route_failed:1;
+        bool dhcp4_route_retrying:1;
         bool dhcp4_configured:1;
         bool dhcp6_configured:1;
 
index 5422b56dc265892c095c782941a6891b7e2f9248..12cf08b5ec0ab41fccab011ac4c17547f80c9e86 100644 (file)
@@ -64,6 +64,8 @@ struct Manager {
         usec_t speed_meter_interval_usec;
         usec_t speed_meter_usec_new;
         usec_t speed_meter_usec_old;
+
+        bool dhcp4_prefix_root_cannot_set_table;
 };
 
 int manager_new(Manager **ret);
index 171df79eca496984f41549d83b19d36aa415eee7..b7afdaa9114893db74fb01adcc13a4ea5a33559b 100755 (executable)
@@ -2469,13 +2469,16 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         output = check_output('ip route show table main dev veth99')
         print(output)
         # See issue #8726
-        self.assertEqual(output, '')
+        main_table_is_empty = output == ''
+        if not main_table_is_empty:
+            self.assertNotRegex(output, 'proto dhcp')
 
         print('## ip route show table 211 dev veth99')
         output = check_output('ip route show table 211 dev veth99')
         print(output)
         self.assertRegex(output, 'default via 192.168.5.1 proto dhcp')
-        self.assertRegex(output, '192.168.5.0/24 proto dhcp')
+        if main_table_is_empty:
+            self.assertRegex(output, '192.168.5.0/24 proto dhcp')
         self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 proto dhcp')
         self.assertRegex(output, '192.168.5.1 proto dhcp scope link')