summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-dhcp4.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2019-06-07 08:31:07 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2019-06-07 09:22:00 +0200
commitd03073ddcde6dd2d5604b70ff4184acbe0a7961a (patch)
tree130ab21e7f752d1766f49f20a71dbc82a97d1355 /src/network/networkd-dhcp4.c
parentnetwork: ignore callback calls when link is in failed state (diff)
downloadsystemd-d03073ddcde6dd2d5604b70ff4184acbe0a7961a.tar.xz
systemd-d03073ddcde6dd2d5604b70ff4184acbe0a7961a.zip
network: assign new DHCP address before removing old lease address
Closes #12676.
Diffstat (limited to 'src/network/networkd-dhcp4.c')
-rw-r--r--src/network/networkd-dhcp4.c145
1 files changed, 122 insertions, 23 deletions
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index ce1e77429b..978378d7b8 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -13,6 +13,34 @@
#include "string-util.h"
#include "sysctl-util.h"
+static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *new_lease, struct in_addr *address);
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address);
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, struct in_addr *address);
+
+void dhcp4_release_old_lease(Link *link) {
+ union in_addr_union address = IN_ADDR_NULL, address_old = IN_ADDR_NULL;
+
+ assert(link);
+
+ if (!link->dhcp_lease_old)
+ return;
+
+ assert(link->dhcp_lease);
+
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old.in);
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in);
+
+ (void) dhcp_remove_routes(link, link->dhcp_lease_old, link->dhcp_lease, &address_old.in);
+
+ if (!in_addr_equal(AF_INET, &address_old, &address)) {
+ (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old.in);
+ (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old.in);
+ }
+
+ link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
+ link_dirty(link);
+}
+
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -33,6 +61,8 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
+ /* New address and routes are configured now. Let's release old lease. */
+ dhcp4_release_old_lease(link);
link_check_ready(link);
}
@@ -182,10 +212,32 @@ static int link_set_dhcp_routes(Link *link) {
return 0;
}
-static int dhcp_remove_routes(Link *link, struct in_addr *address) {
- _cleanup_free_ sd_dhcp_route **routes = NULL;
+static bool route_present_in_routes(const Route *route, sd_dhcp_route **routes, unsigned n_routes) {
+ assert(n_routes == 0 || routes);
+
+ for (unsigned j = 0; j < n_routes; j++) {
+ union in_addr_union a;
+ unsigned char l;
+
+ assert_se(sd_dhcp_route_get_gateway(routes[j], &a.in) >= 0);
+ if (!in_addr_equal(AF_INET, &a, &route->gw))
+ continue;
+ assert_se(sd_dhcp_route_get_destination(routes[j], &a.in) >= 0);
+ if (!in_addr_equal(AF_INET, &a, &route->dst))
+ continue;
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[j], &l) >= 0);
+ if (l != route->dst_prefixlen)
+ continue;
+ return true;
+ }
+
+ return false;
+}
+
+static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *new_lease, struct in_addr *address) {
+ _cleanup_free_ sd_dhcp_route **routes = NULL, **new_routes = NULL;
uint32_t table;
- int n, i, r;
+ int m = 0, n, i, r;
assert(link);
assert(address);
@@ -193,12 +245,19 @@ static int dhcp_remove_routes(Link *link, struct in_addr *address) {
if (!link->network->dhcp_use_routes)
return 0;
- n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
- if (IN_SET(n, 0, -ENODATA)) {
- log_link_debug(link, "DHCP: No routes received from DHCP server: %m");
+ n = sd_dhcp_lease_get_routes(lease, &routes);
+ if (IN_SET(n, 0, -ENODATA))
return 0;
- } else if (n < 0)
- return log_link_error_errno(link, n, "DHCP error: could not get routes: %m");
+ else if (n < 0)
+ return log_link_error_errno(link, n, "DHCP error: Failed to get routes: %m");
+
+ if (new_lease) {
+ m = sd_dhcp_lease_get_routes(new_lease, &new_routes);
+ if (m == -ENODATA)
+ m = 0;
+ else if (m < 0)
+ return log_link_error_errno(link, m, "DHCP error: Failed to get routes: %m");
+ }
table = link_get_dhcp_route_table(link);
@@ -217,13 +276,16 @@ static int dhcp_remove_routes(Link *link, struct in_addr *address) {
route->table = table;
route->scope = route_scope_from_address(route, address);
+ if (route_present_in_routes(route, new_routes, m))
+ continue;
+
(void) route_remove(route, link, NULL);
}
return n;
}
-static int dhcp_remove_router(Link *link, struct in_addr *address) {
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address) {
_cleanup_(route_freep) Route *route_gw = NULL, *route = NULL;
const struct in_addr *router;
uint32_t table;
@@ -235,7 +297,7 @@ static int dhcp_remove_router(Link *link, struct in_addr *address) {
if (!link->network->dhcp_use_routes)
return 0;
- r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+ r = sd_dhcp_lease_get_router(lease, &router);
if (IN_SET(r, 0, -ENODATA)) {
log_link_debug(link, "DHCP: No gateway received from DHCP server.");
return 0;
@@ -279,7 +341,7 @@ static int dhcp_remove_router(Link *link, struct in_addr *address) {
return 0;
}
-static int dhcp_remove_address(Link *link, struct in_addr *address) {
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, struct in_addr *address) {
_cleanup_(address_freep) Address *a = NULL;
struct in_addr netmask;
int r;
@@ -297,7 +359,7 @@ static int dhcp_remove_address(Link *link, struct in_addr *address) {
a->family = AF_INET;
a->in_addr.in = *address;
- if (sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask) >= 0)
+ if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
(void) address_remove(a, link, NULL);
@@ -366,9 +428,9 @@ 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, &address);
- (void) dhcp_remove_router(link, &address);
- (void) dhcp_remove_address(link, &address);
+ (void) dhcp_remove_routes(link, link->dhcp_lease, NULL, &address);
+ (void) dhcp_remove_router(link, link->dhcp_lease, &address);
+ (void) dhcp_remove_address(link, link->dhcp_lease, &address);
(void) dhcp_reset_mtu(link);
(void) dhcp_reset_hostname(link);
@@ -406,6 +468,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
+ /* The new address is configured, and no route is requested.
+ * Let's drop the old lease. */
+ dhcp4_release_old_lease(link);
link_check_ready(link);
}
@@ -601,6 +666,37 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
return 0;
}
+static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
+ int r;
+
+ link->dhcp_lease_old = TAKE_PTR(link->dhcp_lease);
+
+ /* On ip address change, to keep the connectability, we would like to assign new address and
+ * routes, and then release old lease. There are two possible success paths:
+ *
+ * 1. new address and routes are configured.
+ * -> handled by dhcp_release_old_lease() in dhcp4_route_handler().
+ * 2. new address is configured and no route is requested.
+ * -> handled by dhcp_release_old_lease() in dhcp4_address_handler().
+ *
+ * On error in assigning new address and routes, then the link always enters to the failed
+ * state. And link_enter_failed() leads to the DHCP client to be stopped. So,
+ * dhcp_release_old_lease() will be also called by link_stop_clients().
+ */
+
+ r = dhcp_lease_acquired(client, link);
+ if (r < 0) {
+ /* If it fails, then the new address is not configured yet.
+ * So, let's simply drop the old lease. */
+ sd_dhcp_lease_unref(link->dhcp_lease);
+ link->dhcp_lease = TAKE_PTR(link->dhcp_lease_old);
+ (void) dhcp_lease_lost(link);
+ return r;
+ }
+
+ return 0;
+}
+
static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) {
sd_dhcp_lease *lease;
struct in_addr addr;
@@ -671,8 +767,6 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
break;
case SD_DHCP_CLIENT_EVENT_EXPIRED:
- case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
-
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
return 0;
@@ -686,12 +780,17 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
}
}
- if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
- r = dhcp_lease_acquired(client, link);
- if (r < 0) {
- link_enter_failed(link);
- return r;
- }
+ break;
+ case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
+ if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
+ log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
+ return 0;
+ }
+
+ r = dhcp_lease_ip_change(client, link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
}
break;