Skip to content

Commit

Permalink
Merge pull request systemd#29633 from yuwata/dhcp-ipv6-only-mode-foll…
Browse files Browse the repository at this point in the history
…ow-ups

dhcp: several follow-ups for IPv6 only mode
  • Loading branch information
bluca authored Oct 23, 2023
2 parents 2c87b71 + 0bc30a2 commit 28d2c32
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 20 deletions.
73 changes: 60 additions & 13 deletions src/libsystemd-network/sd-dhcp-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v)
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
assert_return(client, -EINVAL);

if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
if (!client->lease)
return -EADDRNOTAVAIL;

if (ret)
Expand Down Expand Up @@ -1661,10 +1661,37 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *message, siz
return 0;
}

static int client_enter_requesting_now(sd_dhcp_client *client) {
assert(client);

client_set_state(client, DHCP_STATE_REQUESTING);
client->attempt = 0;

return event_reset_time(client->event, &client->timeout_resend,
CLOCK_BOOTTIME, 0, 0,
client_timeout_resend, client,
client->event_priority, "dhcp4-resend-timer",
/* force_reset = */ true);
}

static int client_enter_requesting_delayed(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp_client *client = ASSERT_PTR(userdata);
DHCP_CLIENT_DONT_DESTROY(client);
int r;

r = client_enter_requesting_now(client);
if (r < 0)
client_stop(client, r);

return 0;
}

static int client_enter_requesting(sd_dhcp_client *client) {
assert(client);
assert(client->lease);

(void) event_source_disable(client->timeout_resend);

if (client->lease->ipv6_only_preferred_usec > 0) {
if (client->ipv6_acquired) {
log_dhcp_client(client,
Expand All @@ -1675,17 +1702,16 @@ static int client_enter_requesting(sd_dhcp_client *client) {
log_dhcp_client(client,
"Received an OFFER with IPv6-only preferred option, delaying to send REQUEST with %s.",
FORMAT_TIMESPAN(client->lease->ipv6_only_preferred_usec, USEC_PER_SEC));
}

client_set_state(client, DHCP_STATE_REQUESTING);
client->attempt = 0;
return event_reset_time_relative(client->event, &client->timeout_ipv6_only_mode,
CLOCK_BOOTTIME,
client->lease->ipv6_only_preferred_usec, 0,
client_enter_requesting_delayed, client,
client->event_priority, "dhcp4-ipv6-only-mode-timer",
/* force_reset = */ true);
}

return event_reset_time_relative(client->event, &client->timeout_resend,
CLOCK_BOOTTIME,
client->lease->ipv6_only_preferred_usec, 0,
client_timeout_resend, client,
client->event_priority, "dhcp4-resend-timer",
/* force_reset = */ true);
return client_enter_requesting_now(client);
}

static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
Expand Down Expand Up @@ -1887,7 +1913,7 @@ static int client_enter_bound_now(sd_dhcp_client *client, int notify_event) {
return 0;
}

static int client_timeout_ipv6_only_mode(sd_event_source *s, uint64_t usec, void *userdata) {
static int client_enter_bound_delayed(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp_client *client = ASSERT_PTR(userdata);
DHCP_CLIENT_DONT_DESTROY(client);
int r;
Expand All @@ -1906,6 +1932,13 @@ static int client_enter_bound(sd_dhcp_client *client, int notify_event) {
client->start_delay = 0;
(void) event_source_disable(client->timeout_resend);

/* RFC 8925 section 3.2
* If the client is in the INIT-REBOOT state, it SHOULD stop the DHCPv4 configuration process or
* disable the IPv4 stack completely for V6ONLY_WAIT seconds or until the network attachment event,
* whichever happens first.
*
* In the below, the condition uses REBOOTING, instead of INIT-REBOOT, as the client state has
* already transitioned from INIT-REBOOT to REBOOTING after sending a DHCPREQUEST message. */
if (client->state == DHCP_STATE_REBOOTING && client->lease->ipv6_only_preferred_usec > 0) {
if (client->ipv6_acquired) {
log_dhcp_client(client,
Expand All @@ -1920,7 +1953,7 @@ static int client_enter_bound(sd_dhcp_client *client, int notify_event) {
return event_reset_time_relative(client->event, &client->timeout_ipv6_only_mode,
CLOCK_BOOTTIME,
client->lease->ipv6_only_preferred_usec, 0,
client_timeout_ipv6_only_mode, client,
client_enter_bound_delayed, client,
client->event_priority, "dhcp4-ipv6-only-mode",
/* force_reset = */ true);
}
Expand Down Expand Up @@ -2161,9 +2194,11 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
assert_return(sd_dhcp_client_is_running(client), -ESTALE);
assert_return(client->fd >= 0, -EINVAL);

if (!client->lease)
if (client->state != DHCP_STATE_BOUND)
return 0;

assert(client->lease);

client->start_delay = 0;
client->attempt = 1;
client_set_state(client, DHCP_STATE_RENEWING);
Expand Down Expand Up @@ -2316,6 +2351,18 @@ int sd_dhcp_client_set_ipv6_connectivity(sd_dhcp_client *client, int have) {
return 0;
}

int sd_dhcp_client_interrupt_ipv6_only_mode(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
assert_return(sd_dhcp_client_is_running(client), -ESTALE);
assert_return(client->fd >= 0, -EINVAL);

if (sd_event_source_get_enabled(client->timeout_ipv6_only_mode, NULL) <= 0)
return 0;

client_initialize(client);
return client_start(client);
}

int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
int r;

Expand Down
19 changes: 19 additions & 0 deletions src/network/networkd-dhcp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,25 @@ int dhcp4_start_full(Link *link, bool set_ipv6_connectivity) {
return 1;
}

int dhcp4_renew(Link *link) {
assert(link);

if (!link->dhcp_client)
return 0;

/* The DHCPv4 client may have been stopped by the IPv6 only mode. Let's unconditionally restart the
* client if it is not running. */
if (!sd_dhcp_client_is_running(link->dhcp_client))
return dhcp4_start(link);

/* The client may be waiting for IPv6 connectivity. Let's restart the client in that case. */
if (dhcp_client_get_state(link->dhcp_client) != DHCP_STATE_BOUND)
return sd_dhcp_client_interrupt_ipv6_only_mode(link->dhcp_client);

/* Otherwise, send a RENEW command. */
return sd_dhcp_client_send_renew(link->dhcp_client);
}

static int dhcp4_configure_duid(Link *link) {
assert(link);
assert(link->network);
Expand Down
1 change: 1 addition & 0 deletions src/network/networkd-dhcp4.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ int dhcp4_start_full(Link *link, bool set_ipv6_connectivity);
static inline int dhcp4_start(Link *link) {
return dhcp4_start_full(link, true);
}
int dhcp4_renew(Link *link);
int dhcp4_lease_lost(Link *link);
int dhcp4_check_ready(Link *link);

Expand Down
9 changes: 2 additions & 7 deletions src/network/networkd-link-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "bus-message-util.h"
#include "bus-polkit.h"
#include "dns-domain.h"
#include "networkd-dhcp4.h"
#include "networkd-json.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
Expand Down Expand Up @@ -626,13 +627,7 @@ int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error
if (r == 0)
return 1; /* Polkit will call us back */

if (sd_dhcp_client_is_running(l->dhcp_client))
r = sd_dhcp_client_send_renew(l->dhcp_client);
else
/* The DHCPv4 client may have been stopped by the IPv6 only mode. Let's unconditionally
* restart the client here. Note, if the DHCPv4 client is disabled, then dhcp4_start() does
* nothing and returns 0. */
r = dhcp4_start(l);
r = dhcp4_renew(l);
if (r < 0)
return r;

Expand Down
1 change: 1 addition & 0 deletions src/systemd/sd-dhcp-client.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client);
int sd_dhcp_client_send_decline(sd_dhcp_client *client);
int sd_dhcp_client_send_renew(sd_dhcp_client *client);
int sd_dhcp_client_set_ipv6_connectivity(sd_dhcp_client *client, int have);
int sd_dhcp_client_interrupt_ipv6_only_mode(sd_dhcp_client *client);

sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
Expand Down

0 comments on commit 28d2c32

Please sign in to comment.