]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: dhcp4: do not assign new address before old one is not removed
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 14 Jul 2020 16:30:09 +0000 (01:30 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 15 Jul 2020 08:15:25 +0000 (17:15 +0900)
If DHCP4 client lost a lease, and then soon acquire new lease, then
the removal of the old address may not be completed. If that happens,
and the new and old addresses are the same, then the new address will be
considered as a foreign address. Such a situation can occur when the
DHCP4 server is restarted.

This makes networkd wait for the removal of the old address when a new
lease is acquired.

This also makes the link in configuring state when renewing address.

src/network/networkd-dhcp4.c
src/network/networkd-link.h

index 38cf3f287b4f2a1e3ae0113c81c56f0102d4907b..bad7970e2ba3bea96536a3cbd41a0656f320537f 100644 (file)
 #include "sysctl-util.h"
 #include "web-util.h"
 
-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_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback);
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback);
+static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all, link_netlink_message_handler_t callback);
 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);
+static int dhcp4_update_address(Link *link, bool announce);
+static int dhcp4_remove_all(Link *link);
 
 void dhcp4_release_old_lease(Link *link) {
         struct in_addr address = {}, address_old = {};
@@ -40,9 +40,9 @@ void dhcp4_release_old_lease(Link *link) {
         (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old);
         (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
 
-        (void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false);
-        (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false);
-        (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false);
+        (void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false, NULL);
+        (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false, NULL);
+        (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false, NULL);
 
         if (!in4_addr_equal(&address_old, &address))
                 (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL);
@@ -88,17 +88,12 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
 
         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);
-
+                        r = dhcp4_remove_all(link);
+                        if (r < 0)
+                                link_enter_failed(link);
                         return 1;
                 }
                 if (!link->network->dhcp_send_decline)
@@ -177,7 +172,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) {
 
                 r = route_new(&route);
                 if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not allocate route: %m");
+                        return log_link_error_errno(link, r, "Could not allocate route: %m");
 
                 /* Set routes to DNS servers. */
 
@@ -266,7 +261,7 @@ static int link_set_dhcp_routes(Link *link) {
 
                 r = dhcp_prefix_route_from_lease(link->dhcp_lease, table, &address, &prefix_route);
                 if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not create prefix route: %m");
+                        return log_link_error_errno(link, r, "Could not create prefix route: %m");
 
                 r = dhcp_route_configure(&prefix_route, link);
                 if (r < 0)
@@ -344,7 +339,7 @@ static int link_set_dhcp_routes(Link *link) {
 
                         r = route_new(&route_gw);
                         if (r < 0)
-                                return log_link_error_errno(link, r,  "Could not allocate route: %m");
+                                return log_link_error_errno(link, r, "Could not allocate route: %m");
 
                         /* The dhcp netmask may mask out the gateway. Add an explicit
                          * route for the gw host so that we can route no matter the
@@ -401,7 +396,26 @@ static int link_set_dhcp_routes(Link *link) {
         return link_set_dns_routes(link, &address);
 }
 
-static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
+static int dhcp_route_remove(Route *route, Link *link, link_netlink_message_handler_t callback) {
+        int r;
+
+        r = route_remove(route, link, callback);
+        if (r < 0)
+                return r;
+
+        if (callback)
+                link->dhcp4_remove_messages++;
+
+        return 0;
+}
+
+static int dhcp_remove_routes(
+                        Link *link,
+                        sd_dhcp_lease *lease,
+                        const struct in_addr *address,
+                        bool remove_all,
+                        link_netlink_message_handler_t callback) {
+
         _cleanup_free_ sd_dhcp_route **routes = NULL;
         uint32_t table;
         int n, i, r;
@@ -440,13 +454,21 @@ static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_
                 if (!remove_all && set_contains(link->dhcp_routes, route))
                         continue;
 
-                (void) route_remove(route, link, NULL);
+                r = dhcp_route_remove(route, link, callback);
+                if (r < 0)
+                        return r;
         }
 
         return n;
 }
 
-static int dhcp_remove_router(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,
+                        link_netlink_message_handler_t callback) {
+
         _cleanup_(route_freep) Route *route_gw = NULL, *route = NULL;
         const struct in_addr *router;
         uint32_t table;
@@ -484,8 +506,11 @@ static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_
         route_gw->priority = link->network->dhcp_route_metric;
         route_gw->table = table;
 
-        if (remove_all || !set_contains(link->dhcp_routes, route_gw))
-                (void) route_remove(route_gw, link, NULL);
+        if (remove_all || !set_contains(link->dhcp_routes, route_gw)) {
+                r = dhcp_route_remove(route_gw, link, callback);
+                if (r < 0)
+                        return r;
+        }
 
         r = route_new(&route);
         if (r < 0)
@@ -498,8 +523,11 @@ static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_
         route->priority = link->network->dhcp_route_metric;
         route->table = table;
 
-        if (remove_all || !set_contains(link->dhcp_routes, route))
-                (void) route_remove(route, link, NULL);
+        if (remove_all || !set_contains(link->dhcp_routes, route)) {
+                r = dhcp_route_remove(route, link, callback);
+                if (r < 0)
+                        return r;
+        }
 
         Route *rt;
         LIST_FOREACH(routes, rt, link->network->static_routes) {
@@ -512,13 +540,21 @@ static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_
                 if (!remove_all && in4_addr_equal(router, &rt->gw.in))
                         continue;
 
-                (void) route_remove(rt, link, NULL);
+                r = dhcp_route_remove(rt, link, callback);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
 }
 
-static int dhcp_remove_dns_routes(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,
+                        link_netlink_message_handler_t callback) {
+
         const struct in_addr *dns;
         uint32_t table;
         int i, n, r;
@@ -544,7 +580,7 @@ static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct
 
                 r = route_new(&route);
                 if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not allocate route: %m");
+                        return log_link_error_errno(link, r, "Could not allocate route: %m");
 
                 route->family = AF_INET;
                 route->dst.in = dns[i];
@@ -558,7 +594,9 @@ static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct
                 if (!remove_all && set_contains(link->dhcp_routes, route))
                         continue;
 
-                (void) route_remove(route, link, NULL);
+                r = dhcp_route_remove(route, link, callback);
+                if (r < 0)
+                        return r;
         }
 
         if (!link_prefixroute(link)) {
@@ -566,36 +604,18 @@ static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct
 
                 r = dhcp_prefix_route_from_lease(lease, table, address, &prefix_route);
                 if (r < 0)
-                        return log_link_warning_errno(link, r,  "Could not delete prefix route: %m");
+                        return log_link_warning_errno(link, r, "Could not create prefix route: %m");
 
-                if (remove_all || !set_contains(link->dhcp_routes, prefix_route))
-                        (void) route_remove(prefix_route, link, NULL);
+                if (remove_all || !set_contains(link->dhcp_routes, prefix_route)) {
+                        r = dhcp_route_remove(prefix_route, link, callback);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return 0;
 }
 
-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_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring");
-        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,
@@ -621,7 +641,12 @@ static int dhcp_remove_address(
         if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
                 a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
 
-        (void) address_remove(a, link, callback);
+        r = address_remove(a, link, callback);
+        if (r < 0)
+                return r;
+
+        if (callback)
+                link->dhcp4_remove_messages++;
 
         return 0;
 }
@@ -676,8 +701,90 @@ static int dhcp_reset_hostname(Link *link) {
         return 0;
 }
 
+static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(m);
+        assert(link);
+        assert(link->dhcp4_remove_messages > 0);
+
+        link->dhcp4_remove_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -ESRCH)
+                log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 route, ignoring");
+
+        if (link->dhcp4_remove_messages == 0) {
+                r = dhcp4_update_address(link, false);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+
+        return 1;
+}
+
+static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(m);
+        assert(link);
+        assert(link->dhcp4_remove_messages > 0);
+
+        link->dhcp4_remove_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EADDRNOTAVAIL)
+                log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring");
+        else
+                (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+        if (link->dhcp4_remove_messages == 0) {
+                r = dhcp4_update_address(link, false);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+
+        return 1;
+}
+
+static int dhcp4_remove_all(Link *link) {
+        struct in_addr address;
+        int r;
+
+        assert(link);
+        assert(link->dhcp_lease);
+
+        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get DHCPv4 address: %m");
+
+        r = dhcp_remove_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler);
+        if (r < 0)
+                return r;
+
+        r = dhcp_remove_router(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler);
+        if (r < 0)
+                return r;
+
+        r = dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true, dhcp4_remove_route_handler);
+        if (r < 0)
+                return r;
+
+        r = dhcp_remove_address(link, link->dhcp_lease, &address, dhcp4_remove_address_handler);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 static int dhcp_lease_lost(Link *link) {
-        struct in_addr address = {};
+        int r;
 
         assert(link);
         assert(link->dhcp_lease);
@@ -686,13 +793,17 @@ static int dhcp_lease_lost(Link *link) {
 
         link->dhcp4_configured = false;
 
-        (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, NULL);
-        (void) dhcp_reset_mtu(link);
-        (void) dhcp_reset_hostname(link);
+        r = dhcp4_remove_all(link);
+        if (r < 0)
+                return r;
+
+        r = dhcp_reset_mtu(link);
+        if (r < 0)
+                return r;
+
+        r = dhcp_reset_hostname(link);
+        if (r < 0)
+                return r;
 
         link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
         link_dirty(link);
@@ -830,17 +941,21 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
         return 1;
 }
 
-static int dhcp4_update_address(Link *link,
-                                struct in_addr *address,
-                                struct in_addr *netmask,
-                                uint32_t lifetime) {
+static int dhcp4_update_address(Link *link, bool announce) {
         _cleanup_(address_freep) Address *addr = NULL;
+        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
+        struct in_addr address, netmask;
         unsigned prefixlen;
         int r;
 
-        assert(address);
-        assert(netmask);
-        assert(lifetime);
+        assert(link);
+        assert(link->network);
+
+        if (!link->dhcp_lease)
+                return 0;
+
+        link_set_state(link, LINK_STATE_CONFIGURING);
+        link->dhcp4_configured = false;
 
         /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). Before they
          * are called, the related flags must be cleared. Otherwise, the link becomes configured state
@@ -848,18 +963,59 @@ static int dhcp4_update_address(Link *link,
         link->static_routes_configured = false;
         link->static_nexthops_configured = false;
 
-        prefixlen = in4_addr_netmask_to_prefixlen(netmask);
+        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "DHCP error: no address: %m");
+
+        r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
+
+        if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
+                r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
+        }
+
+        prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
+
+        if (announce) {
+                const struct in_addr *router;
+
+                r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+                if (r < 0 && r != -ENODATA)
+                        return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
+
+                if (r > 0 && !in4_addr_is_null(&router[0]))
+                        log_struct(LOG_INFO,
+                                   LOG_LINK_INTERFACE(link),
+                                   LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
+                                                    ADDRESS_FMT_VAL(address),
+                                                    prefixlen,
+                                                    ADDRESS_FMT_VAL(router[0])),
+                                   "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
+                                   "PREFIXLEN=%u", prefixlen,
+                                   "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0]));
+                else
+                        log_struct(LOG_INFO,
+                                   LOG_LINK_INTERFACE(link),
+                                   LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
+                                                    ADDRESS_FMT_VAL(address),
+                                                    prefixlen),
+                                   "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
+                                   "PREFIXLEN=%u", prefixlen);
+        }
 
         r = address_new(&addr);
         if (r < 0)
-                return r;
+                return log_oom();
 
         addr->family = AF_INET;
-        addr->in_addr.in.s_addr = address->s_addr;
+        addr->in_addr.in.s_addr = address.s_addr;
         addr->cinfo.ifa_prefered = lifetime;
         addr->cinfo.ifa_valid = lifetime;
         addr->prefixlen = prefixlen;
-        addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
+        addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
         addr->prefix_route = link_prefixroute(link);
 
         /* allow reusing an existing address and simply update its lifetime
@@ -873,102 +1029,34 @@ static int dhcp4_update_address(Link *link,
 
 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
         sd_dhcp_lease *lease;
-        struct in_addr address;
-        struct in_addr netmask;
-        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
         int r;
 
         assert(link);
         assert(client);
-        assert(link->network);
 
         r = sd_dhcp_client_get_lease(client, &lease);
         if (r < 0)
                 return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
 
         sd_dhcp_lease_unref(link->dhcp_lease);
-        link->dhcp4_configured = false;
         link->dhcp_lease = sd_dhcp_lease_ref(lease);
         link_dirty(link);
 
-        r = sd_dhcp_lease_get_address(lease, &address);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP error: no address: %m");
-
-        r = sd_dhcp_lease_get_netmask(lease, &netmask);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
-
-        if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
-                r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
-        }
-
-        r = dhcp4_update_address(link, &address, &netmask, lifetime);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not update IP address: %m");
-
-        return 0;
+        return dhcp4_update_address(link, false);
 }
 
 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
-        const struct in_addr *router;
         sd_dhcp_lease *lease;
-        struct in_addr address;
-        struct in_addr netmask;
-        unsigned prefixlen;
-        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
         int r;
 
         assert(client);
         assert(link);
 
-        link->dhcp4_configured = false;
-
         r = sd_dhcp_client_get_lease(client, &lease);
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP error: No lease: %m");
 
-        r = sd_dhcp_lease_get_address(lease, &address);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP error: No address: %m");
-
-        r = sd_dhcp_lease_get_netmask(lease, &netmask);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
-
-        prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
-
-        if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
-                r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
-        }
-
-        r = sd_dhcp_lease_get_router(lease, &router);
-        if (r < 0 && r != -ENODATA)
-                return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
-
-        if (r > 0 && !in4_addr_is_null(&router[0]))
-                log_struct(LOG_INFO,
-                           LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
-                                            ADDRESS_FMT_VAL(address),
-                                            prefixlen,
-                                            ADDRESS_FMT_VAL(router[0])),
-                           "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
-                           "PREFIXLEN=%u", prefixlen,
-                           "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0]));
-        else
-                log_struct(LOG_INFO,
-                           LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
-                                            ADDRESS_FMT_VAL(address),
-                                            prefixlen),
-                           "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
-                           "PREFIXLEN=%u", prefixlen);
-
+        sd_dhcp_lease_unref(link->dhcp_lease);
         link->dhcp_lease = sd_dhcp_lease_ref(lease);
         link_dirty(link);
 
@@ -1019,9 +1107,14 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
                 }
         }
 
-        r = dhcp4_update_address(link, &address, &netmask, lifetime);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not update IP address: %m");
+        if (link->dhcp4_remove_messages == 0) {
+                r = dhcp4_update_address(link, true);
+                if (r < 0)
+                        return r;
+        } else
+                log_link_debug(link,
+                               "The link has previously assigned DHCPv4 address or routes. "
+                               "The newly assigned address and routes will set up after old ones are removed.");
 
         return 0;
 }
index 5e5e1c833552007198ed52d4d9c43b131a2d0206..7b39dd91ae5ad0efb88e91235fd3ecc75d66f869 100644 (file)
@@ -100,6 +100,7 @@ typedef struct Link {
         char *lease_file;
         uint32_t original_mtu;
         unsigned dhcp4_messages;
+        unsigned dhcp4_remove_messages;
         unsigned dhcp6_address_messages;
         unsigned dhcp6_route_messages;
         unsigned dhcp6_pd_address_messages;